PC端&移动端:canvas三种方式实现刮刮乐(下)
JS部分
因为我个人想多多练习使用对象,所有这块结构可能并不是最简便的写法。1
2
3
4
5
6
7
8
9
10
11
12function scratcher() {
return this.init();
}
scratcher.prototype = {
init: function() {
this.canvas = document.getElementById('canvas');
this.ctx = this.canvas.getContext('2d');
return this;
},
}
创建了各scratcher对象封装所有变量和方法,最后只需new一下实例就可以展示效果。init作为入口,首先把canvas和画布对象存储下来供后面的方法调用。
涂层绘制
接下来绘制刮刮乐的中奖图片上面的涂层,我使用的是最常见的灰色#ccc
,绘制一个长方形,宽高和canvas相同这样正好全部盖中奖图。如下:1
2
3
4draw: function() {
this.ctx.fillStyle = "#ccc";
this.ctx.fillRect(0, 0, canvas.width, canvas.height);
},
别忘了把这个方法放到init中执行。
事件绑定
然后写涂抹功能scratchHandle
,这部分最后再说,先假设已经有一个scratchHandle
。那么接下来我们将功能事件绑定到鼠标/触摸动作上去:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19bind: function() {
var self = this;
this.canvas.onmousedown = function(){
self.canvas.onmousemove = self.scratchHandle.bind(self);
}
this.canvas.onmouseup = function() {
self.canvas.onmousemove = null;
self.flag = true;
}
this.canvas.addEventListener('touchmove', self.scratchHandle.bind(self), false);
this.canvas.addEventListener('touchend', function(){
self.canvas.removeEventListener('touchmove', self.scratchHandle.bind(self), false);
self.flag = true;
}, false);
}
上面两段绑定的鼠标动作,下面两段绑定的手指触摸动作,记得对应绑定也要解绑,不然就会一直涂抹下去。
bind事件也要加到init中。
关于flag
接下来会提到。
涂抹功能
现在就只剩最核心的涂抹功能了,基本原理就是获得鼠标/触摸点的位置(相对canvas),然后设置图形大小,将涂层抹没。
这里有三种方式,先看代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35scratchHandle: function(e) {
var self = this,
evt = e || window.event,
canvas_location = this.canvas.getBoundingClientRect(),
client_x = evt.clientX || evt.touches[0].clientX,
client_y = evt.clientY || evt.touches[0].clientY,
x = client_x - canvas_location.left,
y = client_y - canvas_location.top;
//方式一
// ctx.clearRect(x-25, y-25, 50, 50);
//方式二
// ctx.globalCompositeOperation = 'destination-out';
// ctx.beginPath();
// ctx.arc(x, y, 25, 0, Math.PI*2, false);
// ctx.fillStyle = 'rgba(0, 0, 0, 1)';
// ctx.fill();
//方式三
this.ctx.globalCompositeOperation = 'destination-out';
this.ctx.lineWidth = 40;
this.ctx.lineCap = "round";
this.ctx.strokeStyle = 'rgba(0, 0, 0, 1)';
if(this.flag) {
self.ctx.beginPath();
self.ctx.moveTo(x, y);
self.flag = false;
}
this.ctx.lineTo(x, y);
this.ctx.stroke();
},
和常用的方法一样,用鼠标/触摸点所在位置的坐标减去canvas左上角左边即为鼠标/触摸点相对canvas的坐标。
获取canvas坐标
不同的是,canvas并不能像div之类的通过offset取坐标值,而需要通过getBoundingClientRect()
去取
获取鼠标/触摸点坐标 evt.touches[0]
是对触摸点坐标值的获取
三种涂抹方式
第一种用的是clearRect方法,可以像橡皮擦一样擦除一定区域的图像,参数分别是橡皮擦的右上角坐标和宽高。缺点就是需要手动模拟中心点,而且形状只能是方形的,还原度不好。
第二种是在涂层上继续绘画,开始想的是设置颜色为透明色,显然只是这样是无法擦出的,后来查到globalCompositeOperation
,这个属性作用是设置重叠区域的效果,有很多属性值可以选择,使用destination-out
就可以将重叠区域“消除”。然后绘制圆形、填充即可。这种方式的明显缺点就是当鼠标移动快的时候会断开,不连续,看起来就是一堆小圆点点。
第三种是比较好的一种,同样是继续绘画,但画的是线。把线设置粗一些,边缘设置成圆角便可获得比较好的效果。为了然线条正常的连接/分开,需要一个flag
标识当前是不是线的起始点,什么时候用moveTo
什么时候用lineTo
都需要在此区分。这也是上面出现flag
的原因,另外需要flag
有一个默认值,跟canvas一起声明即可。
所以最后的init长这样1
2
3
4
5
6
7
8
9
10init: function() {
this.canvas = document.getElementById('canvas');
this.ctx = this.canvas.getContext('2d');
this.flag = true;
this.draw();
this.bind();
return this;
},
全部代码可以到文章开始提到的连接去看,再放一下把连接在此么么哒