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

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

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

刮刮卡抽奖

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

思路

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

相关推荐
mldong4 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排4 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60615 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角6 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!6 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰6 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi8 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen8 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马9 小时前
AI源码分析:拆解模型底层实现逻辑
前端
IT_陈寒9 小时前
React子组件莫名其妙重渲染?你可能漏了这个Hook
前端·人工智能·后端