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

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

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

刮刮卡抽奖

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

思路

将奖品图片转换为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 做出的神奇效果

相关推荐
沐言人生4 分钟前
82.4k 星!把十几万行代码变成知识图谱,新人终于不用硬啃了
前端·后端·github
溪语流沙4 分钟前
【Web全栈进阶】JWT无状态认证:签发、校验、刷新
前端·git·python·github
在繁华处10 分钟前
2.1 上下文:决定 Agent 能力上限的关键
前端·人工智能·microsoft
ndsc_d10 分钟前
2026年有哪些好用的AI UI设计工具?主流工具功能和适用场景对比
前端·人工智能·ui·ai·设计师·ai ui·ai ui工具
溪语流沙32 分钟前
【每天一个CSS | Day18】会摆动的拟物怀表:黄铜、皮革与催眠摆
前端·css3·html5
Lvan的前端笔记38 分钟前
docker:基于docker的前端部署方案
前端·docker·状态模式
程序人生88838 分钟前
从“把隐私文件上传到别人云端“到本地可控:DocConverter Web 立项初心与架构选型
前端·图像处理·人工智能·opencv·架构·ocr·文心一言
开开心心就好1 小时前
视频里的图片怎么提取?双击一下就导出
java·前端·人工智能·spring·智能手机·intellij-idea·excel
爱喝水的小周1 小时前
实验十 Vue‑Router 王者荣耀战绩查询 APP 开发实践
前端·javascript·vue.js
yexianglunbai1 小时前
Vue 3 全面详解:从核心特性到工程化实践
前端·javascript·vue.js