2013年的时候曾经使用canvas实现了一个擦除效果的需求,即模拟用户在模糊的玻璃上擦除水雾看到清晰景色的交互效果。好在2012年的时候学习HTML5的时候研究过canvas了,所以在比较短的时间内实现了一个方案【下文方案一】,后来继续探索之后进一步更新了这个方案【下文方案二】,提高了交互的性能,也提升了用户体验。
今年初的另一个项目,提出了一个比较类似的需求,不过不是擦除效果,需要在一张地图上动态显示雾霾驱散的效果,这个交互需求有个小难点,雾霾的边缘是模糊的,而不是常见的那种整齐的。
这里说明一点,用canvas实现擦除的基本原理是与视觉效果刚好相反的,从视觉和直觉逻辑上看,擦除就是擦掉表层的图像而显露出底层的图案,但是在技术实现上,刚好相反,需要被擦除的图像如模糊的玻璃是直接显示的,而擦除后显示的清晰图案则是在其上绘制的,看上去就像是擦除了模糊的玻璃。
方案一:持续重绘思路下的擦除
这个方案的思路主要是利用canvas的clip方法,该方法可以在指定的位置以特定的形状来裁剪图片,这样就可以实现蒙版效果,因为该方法在调用的时候需要指定位置,因此要实现根据手指或者鼠标动态地指定不同位置的最直接的思路就是canvas动画的基本思路--持续重绘,就是在一个持续不断的循环中调用该接口,传递给该接口的坐标是手指的实际位置。
HTML结构:
从HTML结构可以看出上面所说的【原理相反】:需要被擦除的图片(foo.jpg)是位于底层的,而擦除后显示的图片(bar.jpg)是位于上层的。因为canvas的background样式设置为了透明,这也就从视觉上欺骗了用户,它其实是在上层,但是因为透明,所以除了绘制的部分,其他部分看不见,形成它在下层的错觉。
主体JS代码如下:
function CanvasDoodle(canvas){
this.canvas=canvas;
this.ctx=canvas.getContext("2d");
this.imgSrc=canvas.getAttribute("imgsrc");
this.width=canvas.width;
this.height=canvas.height;
this.left=parseInt(canvas.style.left);
this.top=parseInt(canvas.style.top);
this.touchX=0;
this.touchY=0;
this.requireLoop=false;
this.init();
}
CanvasDoodle.prototype={
init:function(){
document.body.setAttribute("needRefresh","true");
var _self=this;
this.img=new Image();
this.img.src=this.imgSrc;
this.canvas.addEventListener('mousedown',function(e){
e.preventDefault();
_self.requireLoop=true;
_self.touchX= e.clientX-_self.left,_self.touchY= e.clientY-_self.top