function CanvasFade(canvas){
this.canvas=canvas;
this.ctx=canvas.getContext("2d");
this.width=canvas.width;
this.height=canvas.height;
}
CanvasFade.prototype={
draw:function(config){
var _self=this;
var cfg=config?config:{x:200,y:200,r:120};
var ratio=cfg.r/2;
var grd = _self.ctx.createRadialGradient(cfg.x, cfg.y, 0.000, cfg.x, cfg.y, cfg.r);
grd.addColorStop(0.000, 'rgba(255, 0, 0, 0.900)');
grd.addColorStop(0.5, 'rgba(255, 0, 0, 0.600)');
grd.addColorStop(1.0, 'rgba(255, 0, 0, 0.000)');
_self.ctx.fillStyle = grd;
_self.ctx.arc(cfg.x, cfg.y, cfg.r,0,Math.PI*2,true);
_self.ctx.fill();
}
};
var canvasFade=new CanvasFade(Jquery('#theCanvas')[0]);
canvasFade.draw({x:100,y:200y,r:20r});
可以看出实现起来非常简单,这段代码是测试使用的,颜色值是红色,看起来就像热区图,要实现雾霾扩散,只需要更改颜色值即可,实际效果如下:
ps. 这里是多次调用了draw的最终效果。
硬骨头
仅从视觉上看,这两个需求非常接近,因此很容易误以为目标就要实现了。程序开发的一大特点就是[There is more to it then meets the eyes]。那些看起来很炫酷的交互对于开发者可能非常容易实现,因为厂商可能在底层已经实现了。而那些看起来很简单的东西,可能需要花费更多的力气,这也常常成为产品人员和开发人员摩擦的一个原因。
我们来分析需求,雾霾本身覆盖在地图上,并不是均匀的(当然也可以简化成均匀的,这里不是主要困难点),主要问题就是雾霾驱散之后显示出来的是没有雾霾覆盖的地图,而不是纯颜色(可以参看文末gif图片)。边缘的模糊效果就很难实现,因为在设置strokeStyle的时候,如果设置为渐变色,很容易实现边缘模糊,但是就只能用颜色值,如果把strokeStyle设置为pattern就可以使用图片,可是这时就没法设置渐变了,边缘就是整齐切割的,无法满足需求,在反复尝试和求助google之后,终于在stackoverflow上找到了一点线索,貌似有个外国哥们也撞上了类似的需求,不过他非常聪明地绕过了strokeStyle这个问题,所以最终的实现方案就是受到他的启发而实现的,并非由我原创的。
先看代码:
function clipArc(ctx, x, y, r, f) {
var temp = document.createElement('canvas'),
tx = temp.getContext('2d');
temp.width = ctx.canvas.width;
temp.height = ctx.canvas.height;
tx.translate(-temp.width, 0);
tx.shadowOffsetX = temp.width;
tx.shadowOffsetY = 0;
tx.shadowColor = '#000';
tx.shadowBlur = f;
tx.arc(x, y, r, 0, 2 * Math.PI);
tx.closePath();
tx.fill();
ctx.save();
ctx.globalCompositeOperation = 'destination-in';
ctx.drawImage(temp, 0, 0);
ctx.restore();
}
function CanvasFade(canvas){
this.canvas=canvas;
this.ctx=canvas.getContext("2d");
this.imgSrc=canvas.getAttribute("imgSrc");
this.width=canvas.width;
this.height=canvas.height;
}
CanvasFade.prototype={
init:function(config){
var _self=this;
var cfg=config?config:{x:100,y:100,r:120,f:40};
var img=new Image();
img.onload=function(){
var pat=_self.ctx.createPattern(img,"no-repeat");
_self.ctx.fillStyle=pat;
_self.ctx.fillRect(0, 0, _self.width, _self.height);
clipArc(_self.ctx, cfg.x, cfg.y, cfg.r, cfg.f);
};
img.src=this.imgSrc;
}
};
var c=document.querySelector('#theCanvas');
var cf=new CanvasFade(c);
cf.init();