九宫格抽奖,大转盘抽奖,滚动抽奖,刮刮卡抽奖(四)

做了各种各样的营销抽奖活动,有九宫格抽奖、大转盘抽奖、滚动抽奖、刮刮卡抽奖,来总结一波~

九宫格抽奖
大转盘抽奖
滚动抽奖
刮刮卡抽奖

刮刮卡抽奖

我们先来看下最终实现的效果,刮开涂层,看是否中奖:

思路

将奖品图片转换为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>

参考文章

1、学习 canvas 的 globalCompositeOperation 做出的神奇效果

相关推荐
掘金安东尼1 分钟前
7个【宝藏工具】从智能画图到 SSL 自动化,一应俱全
前端·面试·github
zhangxingchao13 分钟前
Flutter屏幕适配指南
前端
石小石Orz18 分钟前
被对象嘲讽,写个网页护眼宝你都不会么
前端
江城开朗的豌豆24 分钟前
🔥 Vue组件传值:小杨教你玩转父子组件通信
前端·javascript·面试
江城开朗的豌豆24 分钟前
Vue组件花式聊天指南:6种传值妙招,总有一款适合你
前端·javascript·vue.js
黑土豆28 分钟前
在Vue3项目中实现PDF文件解析与预览的完整实践
前端·javascript·vue.js
han_34 分钟前
前端如何动态执行JavaScript代码?
前端·javascript
10年前端老司机3 小时前
10道js经典面试题助你找到好工作
前端·javascript
小小小小宇8 小时前
TS泛型笔记
前端
小小小小宇8 小时前
前端canvas手动实现复杂动画示例
前端