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

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

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

刮刮卡抽奖

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

思路

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

相关推荐
秃顶老男孩.27 分钟前
异步处理(前端面试)
前端·面试·职场和发展
三脚猫的喵43 分钟前
微信小程序中实现AI对话、生成3D图像并使用xr-frame演示
前端·javascript·ai作画·微信小程序
文心快码BaiduComate1 小时前
文心快码3.5S全新升级,体验多智能体协同开发,最高赢无人机!
前端·后端·程序员
安卓开发者1 小时前
鸿蒙Next ArkWeb进程解析:多进程架构如何提升Web体验
前端·架构·harmonyos
炒毛豆1 小时前
移动端响应式px转换插件PostCSS的使用~
前端·javascript·postcss
羊羊小栈1 小时前
基于「YOLO目标检测 + 多模态AI分析」的PCB缺陷检测分析系统(vue+flask+数据集+模型训练)
vue.js·人工智能·yolo·目标检测·flask·毕业设计·大作业
恋猫de小郭1 小时前
Flutter Riverpod 3.0 发布,大规模重构下的全新状态管理框架
android·前端·flutter
wordbaby1 小时前
用 window.matchMedia 实现高级响应式开发:API 全面解析与实战技巧
前端·javascript
薄雾晚晴1 小时前
Rspack 实战,构建流程升级:自动版本管理 + 命令行美化 + dist 压缩,一键输出生产包
前端·javascript
huabuyu1 小时前
在 Taro 小程序中实现完整 Markdown 渲染器的实践
前端