代码比较简单,核心部分就是render方法,根据当前鼠标或者手指的位置在canvas的上下文中绘制一个圆形,然后裁剪,这样在下一步drawImage的时候就会在上下文中绘制一个圆形的局部图形而不是整个图片。这样在鼠标或者手指移动的时候就会动态绘制很多小圆,连起来就像是擦除了。
requesetAnimFrame相信大家不会陌生,是个循环调用。这里为了节省性能,设置了一个变量requireLoop来表示是否需要重绘,只有在鼠标按下或者手指接触的时候设置为真,在每个循环中开始重绘canvas(即调用render),结束的时候则设置为假,停止绘制。
这个方案是最原始的方案,有两大缺陷:
一是循环调用,尽管有一个requireLoop可以确定是否重绘,在交互发生的时候始终是在循环,性能并不好;
二是循环调用和用户的交互速度是不同步的,理想状况是手指或者鼠标每发生变化就重绘一次,但是现实并非如此,在非常快速滑动的时候,每次动态获取的坐标并不是紧紧相连的,就造成擦除的效果不是连续的,体验会变差。
方案二:No more loops
方案一的可优化点最明显的就是循环,其两大缺陷都是基于此的。因此方案二的主要思路放弃了clip方法。而是利用了canvas上下文的strokeStyle属性,该属性是指在canvas中绘制矢量图形的时候矢量线的绘制样式,其值可以为color(颜色值)、gradient(渐变对象)、pattern(pattern对象)。这个方案就是将方案一中的drawImg方式改为将canvas上下文的strokeStyle设置为图片,然后在绘制的时候直接画线就可以了,因为矢量线的背景就是需要展示的图片,这样就实现了擦除的效果。HTML结构不变,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.needDraw=false;
this.init();
}
CanvasDoodle.prototype={
init:function(){
var _self=this;
var img=new Image();
img.onload=function(){
var pat=_self.ctx.createPattern(img,"no-repeat");
_self.ctx.strokeStyle=pat;
_self.ctx.lineCap="round";
_self.ctx.lineJoin="round";
_self.ctx.lineWidth="25";
}
img.src=this.imgSrc;
this.canvas.addEventListener('mousedown',function(e){
e.preventDefault();
_self.needDraw=true;
_self.ctx.beginPath();
_self.ctx.moveTo(e.clientX-_self.left,e.clientY-_self.top);
},false);
this.canvas.addEventListener('mousemove',function(e){
e.preventDefault();
if(_self.needDraw){
_self.ctx.lineTo(e.clientX-_self.left,e.clientY-_self.top);
_self.ctx.stroke();
}
},false);
this.canvas.addEventListener('mouseup',function(e){
e.preventDefault();
_self.needDraw=false;
});
}
};
new CanvasDoodle(document.getElementById('CanvasDoodle'));
可以看到,已经没有循环调用了,只是在初始化的时候就设置strokeStyle为图片,在鼠标移动的时候直接lineTo然后stroke就可以了,即简单,又高效,并且即使快速移动鼠标也不会出现锯齿边缘了,因此这个改进的方案完全替代了方案一。效果如下:
新需求,新方案
相信今年年初的雾霾应该是妇孺皆知的了,因为一位前央视记者自费做了一个长期调查然后做了一次演讲,掀起了轩然大波。这个需求正是在这个时期提出的。希望在地图上动态显示雾霾的驱散效果。
先来点简单的
这里我们先分析另外一个略微简单一些的需求,循序渐进。