嗯,好久没有写博客了,因为较长一段时间发生了点点变故:楼主从传统IT项目(OA)跳到了移动端,主要搞微信上的web开发,也是学习了很长一段时间。
言归正传,微信上常见的手指滑动扣个圈圈,然后显示底层图片的应用相信大家也见过不少了。包括微信自身之前也出过付款看图片的应用,也是分分钟刷爆朋友圈。这里,楼主分两个模块从技术上讲解该功能,知道怎么做的可以直接跳过第一部分。
firstBlood,clip的应用。
var canvas = document.getElementById('canvas'),
ctx = canvas.getContent('2d');
ctx.save(); //先保存环境
ctx.beginPath(); //开始路径
ctx.arc(320, winH/2, 80, 0, Math.PI * 2, true); //根据一个坐标点,绘制一个半径为80的圆
ctx.closePath(); //关闭路径
ctx.clip(); //创建一个裁切路径
ctx.clearRect(0, 0, 640, winH); //清除整个画布,由于之前已经clip过了,实际上只清除了圆的范围
ctx.restore(); //还原环境
通过这样一个操作,我们就可以在画布上口出一个半径为80的圆了。效果如下图:
然后结合到各种touch事件,就可以做出擦除效果了,十分简单。
secondBlood,PS画笔工具
那么在该需求上做一下 衍生,PS的画笔工具相信大家也都用过,和前面的最大区别就在于圆的边缘会有一些模糊的效果。如果,需求到这里的话大可以通过canvas的createRadialGradient方法做一个渐变效果实现。接下来大家都懂的,需求远远不会那么简单,既然是做擦除效果,那么当两个圆相交的时候单凭渐变是无法实现该功能的。这个时候,canvas的像素操作技术就该登场咯,let go!
以单次擦除范围为85像素的圆为准,思路如下:
1,先扣除一个半径为55像素的小圆,参照 firstBlood的方法
2,从55像素到85像素,做30次半径累加循环,每次循环都计算出 当前圆心和累加半径所构成的点,并依次增加点的透明度(这里每次循环透明度增加0.033)
具体代码如下:
function renderCanvas(){
var empty = r - 30, curR, x = currentPos.x, y = currentPos.y, imgData, data, i, j, x1, y1, tempVal;
ctx.save();
ctx.beginPath();
ctx.arc(x, y, empty, 0, Math.PI * 2, true);
ctx.closePath();
ctx.clip();
ctx.clearRect(0, 0, 640, winH);
ctx.restore();
//清除了小圆后,获取整个canvas的像素点
imgData = ctxTwo.getImageData(0, 0, 640, winH);
//存储像素点 颜色值的一维数组
data = imgData.data;
for(i = 0; i < 30; i++) {
curR = empty + i;
for(j = 0; j < 361; j++) {
//x1, y1代表当前半径上圆的点坐标
x1 = parseInt(x + curR * Math.cos(j * Math.PI / 180));
y1 = parseInt(y + curR * Math.sin(j * Math.PI / 180));
//出范围了就继续
if(x1 < 0 || y1 < 0 || x1 > 640 || y1 > winH) continue;
//计算出一维数组中,当前点透明度的键值
curPoint = (y1 - 1) * 640 * 4 + x1 * 4 + 3;
//如果透明度已经是0了就不做处理
if(0 == data[curPoint]) continue;
//根据循环的半径做透明度降低处理
tempVal = 255 * (i * 0.033);
//如果计算出的透明度 高于当前的透明度就不做处理。
if(tempVal < data[curPoint]) data[curPoint] = tempVal;
}
}
//清楚整个画布
ctx.clearRect(0, 0, 640, winH);
//将处理过的像素值 在canvas上重绘
ctx.putImageData(imgData, 0, 0);
}
效果如下:
仔细看的话,会发现边缘会有模糊,当然这个范围可以增大 让其更明显。
今天早上发现 android手机上也还是比较流畅特别是 锤子1手机,还是比较满意 O(∩_∩)O~
关于像素点的计算,科普一下
imgData = ctxTwo.getImageData(0, 0, 640, winH);
imgData有一个名为data的一维数组 属性,以640 * 1008大小的canvas为例,
其数组的长度为 640 * 1008 * 4,我们知道rgba的值为4位,前三位表示其rgba的值,第四位为其透明度的值,取值范围是0--255,
我们反复强调data是一维数组,以第一个点为例,
data[0],表示rgba的第一位
data[1],表示rgba的第二位
data[2],表示rgba的第三位
data[3],表示其颜色的透明度
如果我们要知道 230 * 150 这个点的颜色值的话,其在data里面的开始键值为:
key = 229 * 640 * 4 + 150 * 4;
那么对应的颜色值表示依次为
data[key],表示rgba的第一位
data[key + 1],表示rgba的第二位
data[key + 2],表示rgba的第三位
data[3 + 3],表示其颜色的透明度
最后推荐一个canvas api手册网址:
https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API
相关术语以及专业的解释,案例都可以看到,谢谢!