本篇介绍使用canvas实现红包照片的效果。
在前一篇已经介绍了canvas处理图片的一些接口,接下来直接用这些接口实现模糊和偷窥镜的效果。
一、模糊效果
1、获取imageData
前面已经介绍过,通过接口
var imageData = context.getImageData(sx,sy,sw,sh)
获取canvas中的图像数据,所以首先需要一个绘制了原图的canvas,canvas绘制原图采用回调的方式:
Image image = new Image();
image.src = './images/pic.jpg'; // 图片地址
image.onload = function() {
context.drawImage(image, 0, 0, canvas.width, canvas.height);
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
}
2、对imageData进行模糊变换
模糊的算法在前一节已经介绍过,这里可以直接使用即可。需要注意的是imageData的数据结构:
1、imageData是一个数组
2、数组的长度为canvas.height*canvas.width*4
3、数组的4*i~4*i+3连续的四个元素表示一个像素的rgba的值
4、数组代表的像素依次对应图像横向排列的像素,图像的第i行第j列的像素对应数组的(j*canvas.width + i)*4开始的4个元素
3、绘制变换后imageData
context.putImageData(imageData, 0, 0);
二、偷窥镜处理
前面绘制好了模糊图片,现在处理偷窥镜效果需要在其上覆盖另外一层canvas。
1、绘制圆形清晰图片
绘制图片中的一部分,需要使用canvas的裁剪功能。
context.save(); // save和restore在裁剪时使用,避免裁剪路径的重复使用
context.beginPath();
context.arc(x, y, raduis, 0, 2*Math.PI, false); // 弧形的剪裁(需要圆心坐标、半径、弧度和绘制是否逆时针)
context.clip();
context.drawImage(imageData, 0, 0, canvas.width, canvas.height);
context.restore();
2、偷窥镜的动态处理
包括两个方面——改变圆心的位置、改变半径的大小,然后重新绘制新的圆形图像即可,需要注意的是
1、圆心的位置要保证整个圆都包含在canvas中
2、每次重新绘制之前,需要清除画布——context.clearRect(0, 0, canvas.width, canvas.height)
三、算法改善——高斯模糊
1、低效率
非常尴尬的是,一张约400*500的图片做模糊,在良好的环境下居然耗时2秒多,效率很低。
计算一下之前模糊算法复杂度
O(4*width*height*r^2)。
从实现的角度是没有办法处理的,所以必须要改变算法。
算法有两个方面:
1、牺牲效果来提高效率;
2、提高效果。
2、提高效率
由于需要对每个点做模糊处理,所以width和height是无法优化的,只能对每个点的模糊区域做处理,比较普遍的方法是化面为线,即在做平均值时,只取纵向和横向两条线(而不是整个区域),也可以加上两条对角线。这样最终得到的算法复杂度为
O(4*width*height*r*2)。
2、高斯模糊
首先模糊的概念就是每个点的值取周围点值的加权平均值;而我们之前的算法,每个点的权值相等。
而高斯模糊则是根据实际来确定权值,即靠的越近的点权值越大,权值依据正态分布曲线计算,得到二维的Gauss函数如下:
由于前面已经提到二维的效率太低,所以只能老老实实的回到一维到世界了
四、结语
具体效果大家可以参考:http://tkixp9.github.io/redbag/demo_canvas.html 。
五、家庭作业
之前我们讲了css和canvas实现模糊的方法,其实svg也可以实现模糊,大家可以参考http://tkixp9.github.io/redbag/demo_svg.html 来完成对应的模糊和偷窥镜的效果(注意兼容性)。具体的使用,将会在以后的svg章节一起介绍。