canvas实现刮刮乐,带节流效果,兼容移动端
Posted hans774882968
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了canvas实现刮刮乐,带节流效果,兼容移动端相关的知识,希望对你有一定的参考价值。
CSS布局的思路,是让图片绝对定位,并设置z-index=-1
,使得它能被canvas挡住。然后canvas的颜色设为透明,从而让图片显示。
JS部分。对节流函数进行了一些修改。thisArg指定fn的this。节流主要是用在mousemove和touchstart,产生涂抹效果那里。
function throttle(fn, interval, thisArg) {
let waiting = false
return function (...args) {
if (waiting) return
waiting = true
setTimeout(() => {
fn.apply(thisArg, args)
waiting = false
}, interval)
}
}
init函数,因为loadImg是异步方法,索性也给他加上了Promise。
事件绑定的部分,我查到的资料似乎都是在mousedown事件的listener里嵌套mousemove的listener,感觉似乎并没有更好的方案了。
mousemove和touchstart各写一个listener,这是为了兼容移动端。
- mousemove可以直接用
e.offsetX
获得鼠标相对#canvas
的坐标。 - 但touchstart只能
event.touches[0].clientX
获得触屏坐标相对于浏览器窗口左上角的坐标,因此还需要减去offsetLeft、offsetTop,来获得相对#canvas的坐标。
用到canvas的一些api如下
- this.ctx.beginPath(),开始一条路径,或重置当前的路径
- this.ctx.fillStyle = ‘grey’,用来填充颜色,或者提供渐变
- this.ctx.fillRect(0, 0, this.w, this.h)
- this.ctx.arc(x, y, 30, 0, Math.PI * 2),用来画扇形的,半径是30px,起始和结束弧度分别是0和2*pi。
- this.ctx.fill(),紧跟fillStyle
- this.ctx.closePath(),可以用来创建闭合路径
- this.ctx.getImageData(0, 0, this.w, this.h).data,获得像素数据,然后就可以计算有多少点已经透明了。这实现了涂到一定程度就自行消失的效果。
下面的例子展示了canvas画三角形,并设置从黑到白的渐变色。其实我还没完全学懂,代码先放这了
'use strict';
let canvas = $('#canvas')[0]
let ctx = canvas.getContext('2d')
ctx.beginPath()
ctx.moveTo(30, 30)
ctx.lineTo(30, 130)
ctx.lineTo(110, 190)
ctx.closePath()
ctx.stroke()
let colorChange = ctx.createLinearGradient(0, 0, 0, 170)
colorChange.addColorStop(0, "black")
colorChange.addColorStop(1, "white")
ctx.fillStyle = colorChange
ctx.fill()
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>刮开有奖</title>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<link rel="stylesheet" href="index.css">
</head>
<body>
<div class="container">
<img class="img" src="1.jpg">
<canvas id="canvas" width="400" height="300"></canvas>
</div>
</body>
<script src="index.js"></script>
</html>
CSS
body{
margin: 0;
display: flex;
justify-content: center;
}
.container{
margin-top: 100px;
position: relative;
user-select: none;
}
.container .img{
position: absolute;
top: 0;
left: 0;
z-index: -1;
width: 400px;
height: 300px;
}
JS
'use strict';
function throttle(fn, interval, thisArg) {
let waiting = false
return function (...args) {
if (waiting) return
waiting = true
setTimeout(() => {
fn.apply(thisArg, args)
waiting = false
}, interval)
}
}
class ScratchCard {
constructor() {
this.canvas = $('#canvas')[0]
this.img = $('.img')[0]
this.container = $('.container')[0]
this.ctx = this.canvas.getContext('2d')
this.w = this.canvas.width
this.h = this.canvas.height
this.area = this.w * this.h
}
loadImg() {
return new Promise((resolve) => {
let paths = ['1.jpg', '2.jpg']
this.img.onload = resolve
this.img.src = paths[parseInt(Math.random() * paths.length)]
})
}
init() {
return new Promise((resolve) => {
this.ctx.beginPath()
this.ctx.fillStyle = 'grey'
this.ctx.fillRect(0, 0, this.w, this.h)
resolve()
}).then(this.loadImg.bind(this))
}
addEvent() {
// let device = /android|iphone|ipad|ipod|webos|iemobile|opear mini|linux/i.test(navigator.userAgent.toLowerCase())
// let startEvtName = device ? 'touchstart' : 'mousedown'
// let moveEvtName = device ? 'touchmove' : 'mousemove'
// let endEvtName = device ? 'touchend' : 'mouseup'
$(this.canvas).bind('mousedown', () => {
let throttleMove = throttle((e) => {
let x = e.offsetX, y = e.offsetY
// console.log(x, y)//
this.eraseGrey(x, y)
}, 50, this)
$(this.canvas).bind('mousemove', (e) => throttleMove(e))
})
$(this.canvas).bind('touchstart', () => {
let throttleMove = throttle((event) => {
let x = event.touches[0].clientX - this.container.offsetLeft,
y = event.touches[0].clientY - this.container.offsetTop
// console.log(x, y)//
this.eraseGrey(x, y)
}, 50, this)
$(this.canvas).bind('touchmove', () => throttleMove(event))
})
$(this.canvas).bind('mouseup', () => {
$(this.canvas).unbind('mousemove')
})
$(this.canvas).bind('touchend', () => {
$(this.canvas).unbind('touchmove')
})
}
eraseGrey(x, y) {
this.ctx.globalCompositeOperation = 'destination-out'
this.ctx.beginPath()
this.ctx.arc(x, y, 30, 0, Math.PI * 2)
this.ctx.fill()
this.ctx.closePath()
if (this.erasedEnough()) {
this.ctx.clearRect(0, 0, this.w, this.h)
$(this.canvas).unbind('mousedown')
$(this.canvas).unbind('mousemove')
$(this.canvas).unbind('mouseup')
$(this.canvas).unbind('touchstart')
$(this.canvas).unbind('touchmove')
$(this.canvas).unbind('touchend')
}
}
erasedEnough() {
let data = this.ctx.getImageData(0, 0, this.w, this.h).data
let s = 0
for (let i = 3; i < data.length; i += 4) {
if (!data[i]) ++s
}
return s >= 0.8 * this.area
}
}
let sc = new ScratchCard()
sc.init().then(sc.addEvent.bind(sc))
以上是关于canvas实现刮刮乐,带节流效果,兼容移动端的主要内容,如果未能解决你的问题,请参考以下文章