面试官:不使用canvas怎么实现一个刮刮卡效果?

3,587 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第27天,点击查看活动详情

说在前面

🎈刮刮卡大家应该都玩过吧,还记得小时候的小卖部总会有各种各样的刮卡抽奖活动,刮卡时那心跳的感觉让我们更加的欲罢不能,随着时代的发展,现在很多回忆也都有了网页版,通过canvas我们可以很方便的实现一个刮刮卡的功能效果,那么不使用canvas的话呢?我们也可以很快速的实现这个功能!

效果预览

  • canvas模式 JYeontu组件库 - Google Chrome 2022-08-22 17-21-49 00_00_00-00_00_30.gif

  • dom模式 JYeontu组件库 - Google Chrome 2022-08-22 17-26-37 00_00_00-00_00_30.gif

体验地址

jyeontu.xyz/jvuewheel/#…

组件实现

设计图

image.png 我们可以使用若干个小块来拼接成完整的一大块涂层,后面只需要判断鼠标事件,将鼠标划过的小块设置样式透明即可,有了大致的思路之后我们便可以开始动手来开始敲代码了。

参数说明

  • width

刮刮卡的宽度,默认为300px

  • height

刮刮卡的高度,默认为200px

  • mode

刮刮卡的模式,有canvas和dom两种模式,默认为canvas模式

  • color

刮刮卡涂层颜色,默认为gray

  • block

刮刮卡涂层小块配置,mode为dom时需要设置小块的宽度width和高度height

功能实现

一、dom模式

html

刮刮卡的内容通过插槽的方式传入展示。

<div :id="uid + 'j-scratch-card'" class="j-scratch-card">
    <div :id="uid + 'j-scratch-card-bg'" class="j-scratch-card-bg">
        <slot name="j-scratch-card-bg-slot"></slot>
    </div>
    <div
        :id="uid + 'j-scratch-card-mask'"
        class="j-scratch-card-mask"
    ></div>
</div>
初始化刮刮卡样式

通过传入参数初始化刮刮卡样式。

const scratchCardId = this.uid + "j-scratch-card";
const scratchCardDom = document.getElementById(scratchCardId);
scratchCardDom.style.width = this.width;
scratchCardDom.style.height = this.height;
生成刮刮卡涂层小块

计算应该生成的小块行数和列数。

const block = this.block;
const col = Math.ceil(
    parseInt(this.height) / parseInt(block.height)
);
const row = Math.ceil(parseInt(this.width) / parseInt(block.width));
for (let i = 0; i < col; i++) {
    const colDom = document.createElement("div");
    colDom.style = "display:flex;";
    colDom.classList.add("j-scratch-card-mask-col");
    for (let j = 0; j < row; j++) {
        const rowDom = document.createElement("div");
        rowDom.classList.add("j-scratch-card-mask-row");
        rowDom.id = `j-scratch-card-mask-row-${col}-${row}`;
        rowDom.style = `width:${block.width};height:${block.height};background-color: ${this.color};`;
        rowDom.addEventListener("mouseover", e => {
            if (!this.isMouseDown) return;
            e.target.style.opacity = "0";
        });
        colDom.appendChild(rowDom);
    }
    scratchCardMaskDom.appendChild(colDom);
}
监听鼠标事件

监听涂层的鼠标事件。

const scratchCardMaskId = this.uid + "j-scratch-card-mask";
const scratchCardMaskDom = document.getElementById(
    scratchCardMaskId
);
scratchCardMaskDom.addEventListener("mousedown", () => {
    this.isMouseDown = true;
});
window.addEventListener("mouseup", () => {
    this.isMouseDown = false;
});
window.addEventListener("dragend", () => {
    this.isMouseDown = false;
});

二、canvas模式

相比于dom原生实现,canvas实现的效果和性能会更好,所以主流还是使用canvas来实现刮刮卡的效果,所以这里我也实现了canvas模式的刮刮卡。

html
<div style="position: relative;" v-if="mode == 'canvas'">
    <canvas
        :id="uid + '-canvas'"
        :width="parseInt(width)"
        :height="parseInt(height)"
    >
    </canvas>
    <div class="canvas-bg">
        <slot name="j-scratch-card-bg-slot"></slot>
    </div>
</div>
初始化刮刮卡样式

通过传入参数初始化刮刮卡样式。

const canvas = document.getElementById(this.uid + "-canvas");
const ctx = canvas.getContext("2d");
// 填充的颜色
ctx.fillStyle = this.color;
// 填充矩形 fillRect(起始X,起始Y,终点X,终点Y)
ctx.fillRect(0, 0, parseInt(this.width), parseInt(this.height));
监听鼠标事件

监听涂层的鼠标事件。

canvas.onmousedown = () => {
    this.isMouseDown = true;
};
canvas.onmousemove = e => {
    if (!this.isMouseDown) return;
    // 计算鼠标在canvas里的位置
    const x = e.layerX - canvas.offsetLeft;
    const y = e.layerY - canvas.offsetTop;
    // 设置globalCompositeOperation
    ctx.globalCompositeOperation = "destination-out";
    // 画圆
    ctx.arc(x, y, 10, 0, 2 * Math.PI);
    // 填充圆形
    ctx.fill();
};
canvas.onmouseup = () => {
    this.isMouseDown = false;
};

组件库引用

这里我将这个组件打包进了自己的一个组件库,并将其发布到了npm上,有需要的同学也可以直接引入该组件进行使用。
引入教程可以看这里:jyeontu.xyz/jvuewheel/#…
引入后即可直接使用。

源码地址

组件库已开源,想要查看完整源码的可以到 gitee 查看,自己也整理了相关的文档对其进行了简单介绍,具体如下:

组件文档

jvuewheel: jyeontu.xyz/jvuewheel/#…

Gitee源码

Gitee源码:gitee.com/zheng_yongt…

觉得有帮助的同学可以帮忙给我点个star,感激不尽~~~
有什么想法或者改良可以给我提个pr,十分欢迎~~~
有什么问题都可以在评论告诉我~~~

往期精彩

vue封装一个3D轮播图组件

vue实现一个鼠标滑动预览视频封面组件(精灵图版本)

node封装一个图片拼接插件

基于inquirer封装一个控制台文件选择器

node封装一个控制台进度条插件

密码太多不知道怎么记录?不如自己写个密码箱小程序

微信小程序实现一个手势图案锁组件

vue封装一个弹幕组件

为了学(mo)习(yu),我竟开发了这样一个插件

程序员的浪漫之——情侣日常小程序

vue简单实现词云图组件

说在后面

🎉这里是JYeontu,喜欢算法,GDCPC打过卡;热爱羽毛球,大运会打过酱油。毕业一年,两年前端开发经验,目前担任H5前端开发,算法业余爱好者,有空会刷刷算法题,平时喜欢打打羽毛球🏸 ,也喜欢写些东西,既为自己记录📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解🙇,写错的地方望指出,定会认真改进😊,在此谢谢大家的支持,我们下文再见🙌。