canvas讲解进阶篇五

进阶三篇我们讲过了剪裁缩放 所用到的canvas方法也就是drawImage,我们也知道了drawImage方法有三套用法可以直接将图像放置于绘图环境、可以将图像缩放放置于绘图环境也可以将图像剪裁后放置绘图环境。

利用我们讲过的图像剪裁缩放可以制作背景图片滚动效果,例如制作游戏程序时候背景图片的部分显示等功能。大家可以发散思维的。

这篇文章主要讲图像制作包含两个大部分:

离屏canvas以及像素操作

======================华丽分割线===================================

先来说说离屏canvas

我们说过drawImage方法的第一个参数不仅仅可以是image也可以是canvas本身,我们来看看如何将一个canvas绘制到另一个canvas之上的。

将一个canvas内容绘制到另一个canvas上,也就是传说中的离屏canvas,这种情况在很多时候都是很有用的,因为canvas可以保存很多临时的信息,虽然占用了一定内存,但是大大提高了绘图效率。

创建离屏canvas一般经过四个步骤

1.创建用作离屏的canvas元素(不一定添加到页面才叫离屏哈)。

2.设置离屏canvas宽度高度(不设置的话默认300*150)。

3.在离屏canvas中进行绘制。

4.将离屏canvas全部或者一部分复制到当前canvas中

我们来演示一段程序,这段程序中我们利用一个滑动来控制canvas中图片放大倍数,并将超过cavnas部分隐藏,在每次滑动时候都会将离屏中canvas中保存的信息复制到当前canvas中,

为了显示效果我们在图片中添加了两行水印文字,文字随着图片一并缩放。代码中我们引进一个之前没说到过的方法:toDataURL(),这个方法真正的将canvas这个比较另类的东西转化为一副真正图片。

具体用法是我们要在网页中加入一个不可见的图像元素,然后给元素设置id但是不要设置其src属性。然后通过css控制调整图像位置大小使其刚好覆盖在canvas之上。

然后利用toDataURL方法获取数据地址并把数据地址赋给该图像的src属性,然后将图像设为可见 canvas设为不可见。ok,然后我们就可以右键看看发生了什么变化了。先来看主要部分代码:

<script type="text/javascript">

image.οnlοad=function(e){

    context.drawImage(image,0,0,canvas.width,canvas.height);

    offContext.drawImage(image,0,0,canvas.width,canvas.height);

    drawText(context);

    drawText(offContext);

    //myImg.src=canvas.toDataURL();

}

//文字function

function drawText(context){

   

}

//滑动条控制缩放倍数

slider.οnchange=function(e){

    scale=e.target.value;

    if(scale<min_scale)scale=min_scale;

    else if(scale>max_scale)scale=max_scale;

    drawScaled();

}

//缩放效果function

function drawScaled(){

    var w=canvas.width,

        h=canvas.height,

        sw=w*scale,

        sh=h*scale;

    context.drawImage(offCanvas,0,0,offCanvas.width,offCanvas.height,-sw/2+w/2,-sh/2+h/2,sw,sh);

    scaleText.innerText=parseFloat(scale).toFixed(2);

}

</script>

大家可以到这里去下载完整代码

大家可以将代码运行看看效果,通过拖动控制条调整图像显示内容,本机截图如下:

在这段代码中我们将myImg.src=canvas.toDataURL()注释掉,原因是我目前是一个静态网页,页面中引入了一个图片之后再调用toDataURL方法会报出跨域访问的安全性错误。

如果想正常执行的话 可以将程序放置一web服务器下然后用服务启动之,或者我们可以先不引入图片(为了看该方法饮用效果)。我利用了后面的方法 给大家看一下用之前与之后的对比效果。

  

这样通过右键我们就可以将我们的作品保存为图片使用了。

讲完了简单执行操作图像缩放以及离屏canvas后我们来看看怎么操作图像中单个元素。

像素操作(滤镜)

canvas提供了两个方法用于获得和设置图像或者部分图像的像素信息,即getImageData以及putImageData,通过这两个方法我们可以获取像素信息也可以修改已有像素。

先来看一下用法:getImageData方法接受四个参数(x,y,w,h)即要获得的像素范围然后返回一个对象,该对象包含三个属性width以及height和包含各个像素数值的data数组。

在操作图片像素时候如果我们用getImageData方法时候google浏览器会报错原因是涉及到了js安全性问题,也就是你引用的图片涉及到了跨域访问。不过firefox访问没问题,firefox会将整个canvas默认当做一个图片来使用。

进阶篇三种我们介绍过一个裁剪缩放的例子大可用这两个方法来替代。

假设选择矩形对象的rect包含四个值矩形左上角坐标以及宽高。然后我们就可以利用这两个方法进行像素级别操作如下:

var imageData=context.getImageData(rect.left,rect.top,rect.width,rect.height);//获取裁剪区域内的像素集合

context.putImageData(imageData,0,0);将选择像素集合放置canvas左上角并且选区大小不变。

如果我们想要实现将裁剪部分充满整个canavs那么我们就需要利用drawImage传入canvas做第一个参数进行缩放。

ok,我们认识到了操作像素的两个方法,看上去是单纯的捕捉和恢复像素而已。不要以为这么简单的东西,真正的像素操作是在getImageData返回值中的data数组中:

我们来介绍一下这个data数组。正常情况下我们利用getImageData得到一个对象var imgData=context.getImageData(x,y,w,h);var data=imgData.data;

这里我们变量data就是一个数组,这个数组包含你所选择图像范围内每个像素的rgba值。我们知道rgba包含四个值,红蓝绿以及透明度,也就是说每个像素在这个数组里面都会有四个值,如果你选中了十个像素 那么data就会返回包含40个值的数组。

其中每四个为一组。说到数组我们又回到了熟悉的"js语言"中了。待会儿我们就利用这个数组来实现滤镜效果,先不急我们来看看另一个方法putImageData方法,

这个方法在刚才我们替代裁剪时候传三个参数,其实它不仅仅有三个参数,确切的说应该有七个参数,只是后四个默认值而已。

我们先来看看刚才讲到的利用getImageData来实现缩放,每次用户移动鼠标程序都会调用该方法去获得裁剪范围内的像素对象,

这对于浏览器性能来说无疑是消耗巨大的尤其是移动设备这种低配置设备。利用putImageData来实现一个更高效的方式就要应用到后四个参数了,

不过前提是在每次鼠标按下时候都会利用getImageData将canvas中所有像素都捕获到imgData中而不是我们选取的那部分。

我们先来看看后四个参数代表什么东东:用翻译过来的话说 这四个参数确定的是一个脏矩形(dirty rectangle)指的是浏览器将要复制到canvas中的那部分图像数据所占用的区域。

调用格式是putImageData(imageData,x,y,dx,dy,dw,dh);

其中xy表示裁剪的矩形在原canvas中距离左上角(0,0)的偏移量(如果是0就在原位置裁剪,否则根据设置值进行偏移,可以是负数)

后四个参数指的就是我们所谓的裁剪的区域部分(注意这里的区域坐标都是相对于原canvas而言)也就是说将上面实现方式用这种方式替代的话代码如下:

var imageData=context.getImageData(0,0,canvas.width,canvas.height);按下鼠标时候获得整个绘图环境像素对象

context.putImageData(imageData,0,0,rect.left,rect.top,rect.width,rect.height);//移动时候只需操作已获得的像素内容即可

ok我们说完如何使用这两个方法存储并获取图像数据之后再看看怎么修改这些数据(滤镜实现)

说到修改数据我们还是得说说另外一个创建imageData方法也就是createImageData这个方法接受两套参数(x,y)或者(imageData)第一套参数是创建一个长宽为xy的data值都为0的数组,后一套参数作用是将已有的imageData填充到新imageData中。

var newImgData=context.createImageData(canvas.width,canvas.height);这样就创建了一个长宽跟原先canvas一样的空白像素对象。

我们可以利用这个空对象进行对原来canvas像素的拷贝。以及作为imageData传入putImageData中。以实现我们效果。

举个例子我们之前做的裁剪效果可以在裁剪过程中将裁剪区域的透明度降低一半显示(这在ps软件中裁剪可以看到效果)

首先我们要创建一个空的imageData对象

然后将原来canvas对象的imageData中data数据复制给新对象并且在复制过程中将透明度减为原先一半

最后利用putImageData方法进行裁剪出效果。

看大致代码实现如下:

var newImageData=context.createImageData(canvas.width,canvas.height);

var imageData=context.getImageData(0,0,canvas.width,canvas.height);

for(var i=3;i<imageData.data.length-4;i+=4){

  newImageData.data[i]=imageData.data[i]/2;

  newImageData.data[i+1]=imageData.data[i+1];

  newImageData.data[i+2]=imageData.data[i+2];

  newImageData.data[i+3]=imageData.data[i+3];

}

context.putImageData(newImageData,0,0,rect.left,rect.top,rect.width,rect.height);

上面这些代码我们先创建一个空imageData对象然后获得当前canvas中每个像素数据,注意这里的两个对象中data数组长度是一样的。然后重要的就是下面的for循环。

这个for循环从i=3开始 也就是第一个像素的透明度字段,每个四个循环一次(四个值组成完整一个像素),在循环过程中将透明度减半,其他三个红蓝绿复制给新的imageData对象。

最后我们利用putImageData方法将透明度减半的对象进行裁剪并置于canvas之上就得到了我们效果如下:


刚才我们看到了通过for循环就可以遍历像素数据对图片进行透明度修改。同理我们也可以用此方式来实现我们的滤镜效果。下面以一个负片滤镜做演示

做滤镜效果其实比起我们刚才的效果简单不少,因为不用裁剪(当然如果需要裁剪也不会特复杂)。

我们都知道像素的值都在0-255之间,在我们imageData对象中每个像素在data数组中都是四个数据例如前四个data[0]、data[1]、data[2]、data[3],这四个为一组,表示第一个像素的颜色以及透明度,

前三个分别代表色素中的红蓝绿的值,最后一个是透明度。知道了这些负片也就不复杂了,我们只需将前三个值取反即可也就是255-data[i];就达到了我们负片效果,看代码:

for(var i=0;i<imageData.data.length-4;i+=4){

  imageData.data[i]=255-imageData.data[i];

  imageData.data[i+1]=255-imageData.data[i+1];

  imageData.data[i+1]=255-imageData.data[i+1];

}

最后应用putImageData(imageData,0,0)即可,效果如下:



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值