做了各种各样的营销抽奖活动,有九宫格抽奖、大转盘抽奖、滚动抽奖、刮刮卡抽奖,来总结一波~
刮刮卡抽奖
我们先来看下最终实现的效果,刮开涂层,看是否中奖:

思路
将奖品图片转换为canvas
,上面再添加一个灰色的涂层,鼠标移动时慢慢擦掉灰色的涂层。那要怎么擦掉这个灰色的涂层呢?我们需要借助到canvas
的一个属性:globalCompositeOperation
,
globalCompositeOperation
属性设置或返回如何将一个源(新的)图像绘制到目标(已有)的图像上。源图像 = 您打算放置到画布上的绘图。
目标图像 = 您已经放置在画布上的绘图。
该属性有一个属性值 destination-out
:在源图像外显示目标图像。只有源图像外的目标图像部分会被显示,源图像是透明的。看起来是有点懵逼的,我们用实际例子来看下:
静态页面:
bash
<div id="app">
<canvas width="500" height="200" id="canvas"></canvas>
</div>
vue
实现:
ini
<script>
const { createApp, reactive, onMounted, toRefs } = Vue;
createApp({
setup() {
onMounted(() => {
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
ctx.beginPath()
ctx.fillStyle = 'green'
ctx.arc(100, 100, 100, 0, Math.PI * 2, true);
ctx.fill()
ctx.closePath()
ctx.globalCompositeOperation = 'destination-out';
ctx.beginPath()
ctx.arc(200, 100, 100, 0, Math.PI * 2, true);
ctx.fill()
ctx.closePath()
});
},
}).mount("#app");
</script>
可以看到两个圆形相交的地方变透明了:

由上面的例子可知,只要设置该属性,然后以鼠标按下的点为圆心绘制圆形,就可以一点点擦去灰色的涂层。
实现
静态页面:
ini
<div id="app">
<canvas width="400" height="200"></canvas>
</div>
vue
实现:
我们需要先向后台请求接口获取奖品,然后将其绘制成 canvas
的背景图片,再绘制一个灰色的长方形将其遮住,最后再监听鼠标的按下、移动、抬起事件,计算鼠标按下点的位置,已该位置为中心绘制圆形。
ini
<script>
const { createApp, reactive, toRefs, onMounted, nextTick } = Vue
createApp({
setup() {
const state = reactive({
lotteryImg: null, // 奖品图片
mousedown: false, // 鼠标是否按下
ctx: null, // canvas的上下文对象
})
onMounted(() => {
nextTick(() => {
state.canvas = document.querySelector('canvas');
state.lotteryImg = new Image();
state.lotteryImg.src = './prize.jpg';
// 监听图片的加载完成事件
state.lotteryImg.addEventListener('load', handleImgLoaded)
// 移动端触摸事件
state.canvas.addEventListener('touchstart', eventDown);
state.canvas.addEventListener('touchend', eventUp);
state.canvas.addEventListener('touchmove', eventMove);
// 网页版触摸事件
state.canvas.addEventListener('mousedown', eventDown);
state.canvas.addEventListener('mouseup', eventUp);
state.canvas.addEventListener('mousemove', eventMove);
})
})
function handleImgLoaded(e) {
// 绘制奖品图片
state.canvas.style.backgroundImage = 'url(' + state.lotteryImg.src + ')';
state.canvas.style.backgroundSize="cover"
// 绘制灰色涂层(源图像)
state.ctx = state.canvas.getContext('2d');
state.ctx.fillStyle = 'gray';
state.ctx.fillRect(0, 0, 400, 200)
state.ctx.globalCompositeOperation = 'destination-out';
}
// 鼠标按下
function eventDown(e) {
e.preventDefault();
state.mousedown = true;
}
// 鼠标抬起
function eventUp(e) {
e.preventDefault();
state.mousedown = false;
}
// 鼠标移动
function eventMove(e) {
if (state.mousedown) {
// changedTouches:涉及当前(引发)事件的触摸点列表
if (e.changedTouches) {
e = e.changedTouches[e.changedTouches.length - 1];
}
/*
e.pageX,e.pageY坐标相对于整个渲染页面的左上角(包括滚动隐藏距离)
也可以采用 e.clientX + (document.body.scrollLeft || document.documentElement.scrollTop)
e.clientX,e.clientY设置或获取鼠标指针位置相对于浏览器窗口(内容区域的左上角)的坐标,与浏览器是否有滚动条无关
*/
/*
offsetLeft:距离上一级定位元素左侧的距离
offsetTop:距离上一级定位元素上侧的距离
*/
const x = e.pageX - state.canvas.offsetLeft
const y = e.pageY - state.canvas.offsetTop
// 绘制圆形(目标图像)进行擦除
state.ctx.beginPath()
state.ctx.arc(x, y, 20, 0, Math.PI * 2);
state.ctx.fill()
}
}
return {
...toRefs(state)
}
}
}).mount('#app')
</script>