前言
产品给了一个H5需求就是实现一个类似刮刮乐的效果,所以咱们就来研究研究一下!
踩坑
移动端跟PC端监听鼠标移动是不一样的,PC是mousemove,移动端是touchmove
伪代码
- 上面的图层遮盖内容并且可以刮的;
- 下面的图层就是对应的内容;
- 咱们选择canvas来当做上面的图层;
一、先实现上下两个图层
<template>
<div class="container">
<div class="content">
谢谢惠顾
</div>
<canvas />
</div>
</template>
<style scoped lang="less">
.container {
position: relative;
width: max-content;
// 把内容的图层定位,并且把层级调到底部
.content {
position: absolute;
z-index: -1;
width: 200px;
height: 150px;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 24px;
background-color: skyblue;
}
}
</style>
通过上面代码就变成这个效果,因为画布还没填充内容。
二、给画布填充内容
<script setup lang="ts">
import { ref } from 'vue'
const cancasRef = ref<HTMLCanvasElement>()
const ctx = ref<CanvasRenderingContext2D | null>()
onMounted(() => {
if (cancasRef.value) {
ctx.value = cancasRef.value.getContext('2d')
/** 绘制图层 */
ctx.value?.beginPath()
/** 填充颜色 */
ctx.value!.fillStyle = 'red'
/** 设置画布大小 */
ctx.value?.fillRect(0, 0, 200, 150)
}
}
<script>
<template>
<div class="container">
<div class="content">
谢谢惠顾
</div>
<canvas ref="cancasRef" />
</div>
</template>
<style scoped lang="less">
.container {
position: relative;
width: max-content;
// 把内容的图层定位,并且把层级调到底部
.content {
position: absolute;
z-index: -1;
width: 200px;
height: 150px;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 24px;
background-color: skyblue;
}
}
</style>
给画布填充颜色之后就开始遮盖住下面的内容了,那么咱们下一步就是开始刮画布的图层。
三、给画布图层添加刮刮乐的效果
<script setup lang="ts">
import { ref } from 'vue'
const cancasRef = ref<HTMLCanvasElement>()
const ctx = ref<CanvasRenderingContext2D | null>()
/* 获取该元素到可视窗口的距离 */
function getElementWindowRange(element: HTMLCanvasElement) {
let left = 0, top = 0
function get(obj: HTMLCanvasElement){
left += obj.offsetLeft
top += obj.offsetTop
/* 不到最外层就一直调用,直到offsetParent为body*/
if (obj!.offsetParent!.tagName != 'BODY') {
get(obj?.offsetParent as HTMLCanvasElement)
}
return [left, top]
}
return get(element)
}
onMounted(() => {
if (cancasRef.value) {
ctx.value = cancasRef.value.getContext('2d')
/** 用于计算鼠标移动的 */
let offsetX = 0
let offsetY = 0
/** 绘制图层 */
ctx.value?.beginPath()
/** 填充颜色 */
ctx.value!.fillStyle = 'red'
/** 设置画布大小 */
ctx.value?.fillRect(0, 0, 200, 150)
cancasRef.value.addEventListener("touchstart", (event) => {
/** 获取滑动位置 */
const arr = getElementWindowRange(cancasRef.value!)
offsetX = arr[0]
offsetY = arr[1]
/** 橡皮擦效果 */
ctx.value!.globalCompositeOperation = 'destination-out'
/* 画笔粗细*/
ctx.value!.lineWidth = lineWidth
ctx.value!.beginPath()
/* 移动画笔原点*/
ctx.value!.moveTo(event.touches[0].pageX - offsetX, event.touches[0].pageY - offsetY)
})
/** 监听鼠标移动事件 */
cancasRef.value.addEventListener("touchmove", (event) => {
/* 根据手指移动画线,使之变透明*/
ctx.value!.lineTo(event.touches[0].pageX - offsetX, event.touches[0].pageY - offsetY)
/* 填充*/
ctx.value!.stroke()
})
}
}
<script>
<template>
<div class="container">
<div class="content">
谢谢惠顾
</div>
<canvas ref="cancasRef" />
</div>
</template>
<style scoped lang="less">
.container {
position: relative;
width: max-content;
// 把内容的图层定位,并且把层级调到底部
.content {
position: absolute;
z-index: -1;
width: 200px;
height: 150px;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 24px;
background-color: skyblue;
}
}
</style>
通过上面代码我们就得到这个效果,这么一看好像有那味儿了。
四、刮到百分之多少之后清空画布
添加一个监听鼠标松开事件和清空的函数
<script setup lang="ts">
import { ref } from 'vue'
const cancasRef = ref<HTMLCanvasElement>()
const ctx = ref<CanvasRenderingContext2D | null>()
/* 获取该元素到可视窗口的距离 */
function getElementWindowRange(element: HTMLCanvasElement) {
let left = 0, top = 0
function get(obj: HTMLCanvasElement){
left += obj.offsetLeft
top += obj.offsetTop
/* 不到最外层就一直调用,直到offsetParent为body*/
if (obj!.offsetParent!.tagName != 'BODY') {
get(obj?.offsetParent as HTMLCanvasElement)
}
return [left, top]
}
return get(element)
}
/** 清除涂层 */
function clear(alpha: number, width: number, height: number) {
return () => {
ctx.value!.save()
/** 使用谈出的效果 */
ctx.value!.globalCompositeOperation = "source-in"
ctx.value!.fillStyle = ctx.value!.fillStyle + (alpha -= 1).toString(16)
ctx.value!.fillRect(0, 0, width, height)
ctx.value!.restore()
/** 到210已经看不到涂层了 */
if (alpha > 210) requestAnimationFrame(clear(alpha, width, height))
}
}
onMounted(() => {
if (cancasRef.value) {
ctx.value = cancasRef.value.getContext('2d')
/** 用于计算鼠标移动的 */
let offsetX = 0
let offsetY = 0
/** 绘制图层 */
ctx.value?.beginPath()
/** 填充颜色 */
ctx.value!.fillStyle = 'red'
/** 设置画布大小 */
ctx.value?.fillRect(0, 0, 200, 150)
cancasRef.value.addEventListener("touchstart", (event) => {
/** 获取滑动位置 */
const arr = getElementWindowRange(cancasRef.value!)
offsetX = arr[0]
offsetY = arr[1]
/** 橡皮擦效果 */
ctx.value!.globalCompositeOperation = 'destination-out'
/* 画笔粗细*/
ctx.value!.lineWidth = lineWidth
ctx.value!.beginPath()
/* 移动画笔原点*/
ctx.value!.moveTo(event.touches[0].pageX - offsetX, event.touches[0].pageY - offsetY)
})
/** 监听鼠标移动事件 */
cancasRef.value.addEventListener("touchmove", (event) => {
/* 根据手指移动画线,使之变透明*/
ctx.value!.lineTo(event.touches[0].pageX - offsetX, event.touches[0].pageY - offsetY)
/* 填充*/
ctx.value!.stroke()
})
}
/** 监听鼠标松开事件 */
cancasRef.value.addEventListener("touchend", () => {
/** (getImageData)该方法会返回canvas像素点对象 */
const pixelObj = ctx.value!.getImageData(0, 0, width, height)
/** 拿到大小用于遍历 */
const pixel = pixelObj.width * pixelObj.height
/** 记录已经刮开的像素点 */
let transparentPixels = 0
for(let i = 0; i < pixel; i++) {
/**
* 1.data属性为一个数组,每4个元素对应一个像素点
* 2.可以根据像素点的opcity值来判断这个像素点是不是透明,是不是等于0?
*/
if (pixelObj.data[i * 4 + 3] === 0) {
transparentPixels++
}
}
/** 0.7表示挂到70%就清空画布 */
if(transparentPixels >= pixel * 0.7) {
requestAnimationFrame(clear(255, width, height))
}
}, false)
}
<script>
<template>
<div class="container">
<div class="content">
谢谢惠顾
</div>
<canvas ref="cancasRef" />
</div>
</template>
<style scoped lang="less">
.container {
position: relative;
width: max-content;
// 把内容的图层定位,并且把层级调到底部
.content {
position: absolute;
z-index: -1;
width: 200px;
height: 150px;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 24px;
background-color: skyblue;
}
}
</style>
通过上面的代码就得到这个效果,到这里基本也是一个完整的Demo了。
五、对组件进行优化
优化内容
- 添加填充图片;
- 自定义画笔的小大;
- 自定义刮到n%就清空画布;
- 根据内容的大小给画布填充大小,而不是写死;
- 清空完成之后的回调函数;
- 是否可以刮;
添加一个TS文件:index.types.ts
export type Props = {
/**
* 填充类型
* (1) 图片
* (2) 背景颜色
*/
fillType: 1 | 2;
/** 如果上面(fillType)选择图片就传图片的url,否则传背景颜色 */
fillContent: string;
/** 画笔半径(默认20) */
lineWidth?: number;
/** 刮到百分比的时候清空画布(默认50) */
clearZoneRadius?: number;
/** 是否可以刮刮乐(默认是ture) */
isCanDraw?: boolean;
}
export type Emits = {
(e: 'clearFinish'): void;
}
index.vue
<script setup lang="ts">
import { ref, defineProps } from 'vue'
import { type Props, Emits } from './index.types'
const {
fillType,
fillContent,
lineWidth = 20,
clearZoneRadius = 50,
isCanDraw = true
} = defineProps<Props>()
const emit = defineEmits<Emits>()
const cancasRef = ref<HTMLCanvasElement>()
const ctx = ref<CanvasRenderingContext2D | null>()
const contentRef = ref<HTMLDivElement>()
/* 获取该元素到可视窗口的距离 */
function getElementWindowRange(element: HTMLCanvasElement) {
let left = 0, top = 0
function get(obj: HTMLCanvasElement){
left += obj.offsetLeft
top += obj.offsetTop
/* 不到最外层就一直调用,直到offsetParent为body*/
if (obj!.offsetParent!.tagName != 'BODY') {
get(obj?.offsetParent as HTMLCanvasElement)
}
return [left, top]
}
return get(element)
}
/** 清除涂层 */
function clear(alpha: number, width: number, height: number) {
return () => {
ctx.value!.save()
/** 使用谈出的效果 */
ctx.value!.globalCompositeOperation = "source-in"
ctx.value!.fillStyle = ctx.value!.fillStyle + (alpha -= 1).toString(16)
ctx.value!.fillRect(0, 0, width, height)
ctx.value!.restore()
/** 到210已经看不到涂层了 */
if (alpha > 210) requestAnimationFrame(clear(alpha, width, height))
/** 模仿清除完成之后的回调,有大佬知道这块怎么优化吗?知道的话帮忙指点一下,谢谢! */
if (alpha === 211) {
setTimeout(() => {
emit('clearFinish')
}, 200)
}
}
}
onMounted(() => {
if (cancasRef.value) {
ctx.value = cancasRef.value.getContext('2d')
/** 用于计算鼠标移动的 */
let offsetX = 0
let offsetY = 0
/** 获取底下图层的宽高 */
const width = contentRef.value?.offsetWidth || 0
const height = contentRef.value?.offsetHeight || 0
cancasRef.value.width = width
cancasRef.value.height = height
/** 绘制图层 */
ctx.value?.beginPath()
/** 判断一下填充图片还是背景色 */
if (fillType === 1) {
const img = new Image()
img.src = fillContent
img.onload = function() {
ctx.value!.drawImage(img, 0, 0, width, height)
}
} else {
/** 填充颜色 */
ctx.value!.fillStyle = fillContent
}
/** 设置画布大小 */
ctx.value?.fillRect(0, 0, 200, 150)
cancasRef.value.addEventListener("touchstart", (event) => {
/** 获取滑动位置 */
const arr = getElementWindowRange(cancasRef.value!)
offsetX = arr[0]
offsetY = arr[1]
/** 橡皮擦效果 */
ctx.value!.globalCompositeOperation = 'destination-out'
/* 画笔粗细*/
ctx.value!.lineWidth = lineWidth
ctx.value!.beginPath()
/* 移动画笔原点*/
ctx.value!.moveTo(event.touches[0].pageX - offsetX, event.touches[0].pageY - offsetY)
})
/** 监听鼠标移动事件 */
cancasRef.value.addEventListener("touchmove", (event) => {
/* 根据手指移动画线,使之变透明*/
ctx.value!.lineTo(event.touches[0].pageX - offsetX, event.touches[0].pageY - offsetY)
/* 填充*/
ctx.value!.stroke()
})
}
/** 监听鼠标松开事件 */
cancasRef.value.addEventListener("touchend", () => {
/** (getImageData)该方法会返回canvas像素点对象 */
const pixelObj = ctx.value!.getImageData(0, 0, width, height)
/** 拿到大小用于遍历 */
const pixel = pixelObj.width * pixelObj.height
/** 记录已经刮开的像素点 */
let transparentPixels = 0
for(let i = 0; i < pixel; i++) {
/**
* 1.data属性为一个数组,每4个元素对应一个像素点
* 2.可以根据像素点的opcity值来判断这个像素点是不是透明,是不是等于0?
*/
if (pixelObj.data[i * 4 + 3] === 0) {
transparentPixels++
}
}
if(transparentPixels >= pixel * (clearZoneRadius / 100)) {
requestAnimationFrame(clear(255, width, height))
}
}, false)
}
<script>
<template>
<div class="container">
<div class="content" ref="contentRef">
<slot />
</div>
<canvas ref="cancasRef" />
<div class="mask" v-if="!isCanDraw"></div>
</div>
</template>
<style scoped lang="less">
.container {
position: relative;
width: max-content;
// 把内容的图层定位,并且把层级调到底部
.content {
position: absolute;
z-index: -1;
width: 200px;
height: 150px;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 24px;
background-color: skyblue;
}
.mask {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
}
</style>
使用该组件
<script setup lang="ts">
import ScratchCard from '@/components/scratch-card/index.vue'
const clearFinish = () => {
console.log('....')
}
</script>
<template>
<div class="scratch-card">
<ScratchCard
@clear-finish="clearFinish"
:clear-zone-radius="70"
:fill-type="2"
fill-content="green">
<div class="test">
谢谢惠顾
</div>
</ScratchCard>
</div>
</template>
<style scoped lang="less">
.scratch-card {
.test {
height: 150px;
width: 200px;
background-color: skyblue;
display: flex;
align-items: center;
justify-content: center;
color: red;
font-size: 24px;
}
}
</style>
组件使用没什么问题,那么咱们试一试填充图片看看
六、填充图片
组件代码还是一样的,这里就只显示使用组件的代码了
<ScratchCard
@clear-finish="clearFinish"
:clear-zone-radius="70"
:fill-type="1"
fill-content="https://img1.baidu.com/it/u=1105035751,396112046&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1740157200&t=b8a8c2d988f546ace7562f268c9b48c5">
<div class="test">
谢谢惠顾
</div>
</ScratchCard>
看看填充图片的效果如何,咱们看见刮是没什么问题,但是会出现一个报错,这个报错也会导致不能清空,那么咱们就解决一下。
刮刮乐组件代码
加了这行代码 img.crossOrigin = "Anonymous"
<script setup lang="ts">
import { ref, defineProps } from 'vue'
import { type Props, Emits } from './index.types'
const {
fillType,
fillContent,
lineWidth = 20,
clearZoneRadius = 50,
isCanDraw = true
} = defineProps<Props>()
const emit = defineEmits<Emits>()
const cancasRef = ref<HTMLCanvasElement>()
const ctx = ref<CanvasRenderingContext2D | null>()
const contentRef = ref<HTMLDivElement>()
/* 获取该元素到可视窗口的距离 */
function getElementWindowRange(element: HTMLCanvasElement) {
let left = 0, top = 0
function get(obj: HTMLCanvasElement){
left += obj.offsetLeft
top += obj.offsetTop
/* 不到最外层就一直调用,直到offsetParent为body*/
if (obj!.offsetParent!.tagName != 'BODY') {
get(obj?.offsetParent as HTMLCanvasElement)
}
return [left, top]
}
return get(element)
}
/** 清除涂层 */
function clear(alpha: number, width: number, height: number) {
return () => {
ctx.value!.save()
/** 使用谈出的效果 */
ctx.value!.globalCompositeOperation = "source-in"
ctx.value!.fillStyle = ctx.value!.fillStyle + (alpha -= 1).toString(16)
ctx.value!.fillRect(0, 0, width, height)
ctx.value!.restore()
/** 到210已经看不到涂层了 */
if (alpha > 210) requestAnimationFrame(clear(alpha, width, height))
/** 模仿清除完成之后的回调,有大佬知道这块怎么优化吗?知道的话帮忙指点一下,谢谢! */
if (alpha === 211) {
setTimeout(() => {
emit('clearFinish')
}, 200)
}
}
}
onMounted(() => {
if (cancasRef.value) {
ctx.value = cancasRef.value.getContext('2d')
/** 用于计算鼠标移动的 */
let offsetX = 0
let offsetY = 0
/** 获取底下图层的宽高 */
const width = contentRef.value?.offsetWidth || 0
const height = contentRef.value?.offsetHeight || 0
cancasRef.value.width = width
cancasRef.value.height = height
/** 绘制图层 */
ctx.value?.beginPath()
/** 判断一下填充图片还是背景色 */
if (fillType === 1) {
const img = new Image()
/** 防止画布已被跨源数据污染。 */
img.crossOrigin = "Anonymous"
img.src = fillContent
img.onload = function() {
ctx.value!.drawImage(img, 0, 0, width, height)
}
} else {
/** 填充颜色 */
ctx.value!.fillStyle = fillContent
}
/** 设置画布大小 */
ctx.value?.fillRect(0, 0, 200, 150)
cancasRef.value.addEventListener("touchstart", (event) => {
/** 获取滑动位置 */
const arr = getElementWindowRange(cancasRef.value!)
offsetX = arr[0]
offsetY = arr[1]
/** 橡皮擦效果 */
ctx.value!.globalCompositeOperation = 'destination-out'
/* 画笔粗细*/
ctx.value!.lineWidth = lineWidth
ctx.value!.beginPath()
/* 移动画笔原点*/
ctx.value!.moveTo(event.touches[0].pageX - offsetX, event.touches[0].pageY - offsetY)
})
/** 监听鼠标移动事件 */
cancasRef.value.addEventListener("touchmove", (event) => {
/* 根据手指移动画线,使之变透明*/
ctx.value!.lineTo(event.touches[0].pageX - offsetX, event.touches[0].pageY - offsetY)
/* 填充*/
ctx.value!.stroke()
})
}
/** 监听鼠标松开事件 */
cancasRef.value.addEventListener("touchend", () => {
/** (getImageData)该方法会返回canvas像素点对象 */
const pixelObj = ctx.value!.getImageData(0, 0, width, height)
/** 拿到大小用于遍历 */
const pixel = pixelObj.width * pixelObj.height
/** 记录已经刮开的像素点 */
let transparentPixels = 0
for(let i = 0; i < pixel; i++) {
/**
* 1.data属性为一个数组,每4个元素对应一个像素点
* 2.可以根据像素点的opcity值来判断这个像素点是不是透明,是不是等于0?
*/
if (pixelObj.data[i * 4 + 3] === 0) {
transparentPixels++
}
}
if(transparentPixels >= pixel * (clearZoneRadius / 100)) {
requestAnimationFrame(clear(255, width, height))
}
}, false)
}
<script>
<template>
<div class="container">
<div class="content" ref="contentRef">
<slot />
</div>
<canvas ref="cancasRef" />
<div class="mask" v-if="!isCanDraw"></div>
</div>
</template>
<style scoped lang="less">
.container {
position: relative;
width: max-content;
// 把内容的图层定位,并且把层级调到底部
.content {
position: absolute;
z-index: -1;
width: 200px;
height: 150px;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 24px;
background-color: skyblue;
}
.mask {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
}
</style>
加了上面那行代码之后就不会报错了,这里就不演示了,需要的小伙伴就自行看一下就行。
七、解决警告问题
咱们会发现这里会有三个警告
警告1和2就再监听事件哪一行代码就行,在监听事件的第三个参数
{ passive: true }
警告3也是一行代码的事
ctx.value = cancasRef.value.getContext('2d', { willReadFrequently: true })
刮刮乐组件的代码
<script setup lang="ts">
import { ref, defineProps } from 'vue'
import { type Props, Emits } from './index.types'
const {
fillType,
fillContent,
lineWidth = 20,
clearZoneRadius = 50,
isCanDraw = true
} = defineProps<Props>()
const emit = defineEmits<Emits>()
const cancasRef = ref<HTMLCanvasElement>()
const ctx = ref<CanvasRenderingContext2D | null>()
const contentRef = ref<HTMLDivElement>()
/* 获取该元素到可视窗口的距离 */
function getElementWindowRange(element: HTMLCanvasElement) {
let left = 0, top = 0
function get(obj: HTMLCanvasElement){
left += obj.offsetLeft
top += obj.offsetTop
/* 不到最外层就一直调用,直到offsetParent为body*/
if (obj!.offsetParent!.tagName != 'BODY') {
get(obj?.offsetParent as HTMLCanvasElement)
}
return [left, top]
}
return get(element)
}
/** 清除涂层 */
function clear(alpha: number, width: number, height: number) {
return () => {
ctx.value!.save()
/** 使用谈出的效果 */
ctx.value!.globalCompositeOperation = "source-in"
ctx.value!.fillStyle = ctx.value!.fillStyle + (alpha -= 1).toString(16)
ctx.value!.fillRect(0, 0, width, height)
ctx.value!.restore()
/** 到210已经看不到涂层了 */
if (alpha > 210) requestAnimationFrame(clear(alpha, width, height))
if (alpha === 211) {
setTimeout(() => {
emit('clearFinish')
}, 200)
}
}
}
onMounted(() => {
if (cancasRef.value) {
ctx.value = cancasRef.value.getContext('2d', { willReadFrequently: true })
let offsetX = 0
let offsetY = 0
/** 获取底下图层的宽高 */
const width = contentRef.value?.offsetWidth || 0
const height = contentRef.value?.offsetHeight || 0
cancasRef.value.width = width
cancasRef.value.height = height
/** 绘制图层 */
ctx.value?.beginPath()
/** 判断一下填充图片还是背景色 */
if (fillType === 1) {
const img = new Image()
/** 防止画布已被跨源数据污染。 */
img.crossOrigin = "Anonymous"
img.src = fillContent
img.onload = function() {
ctx.value!.drawImage(img, 0, 0, width, height)
}
} else {
/** 填充颜色 */
ctx.value!.fillStyle = fillContent
}
/** 设置画布大小 */
ctx.value?.fillRect(0, 0, width, height)
/** 监听鼠标按下事件 */
cancasRef.value.addEventListener("touchstart", (event) => {
/** 获取滑动位置 */
const arr = getElementWindowRange(cancasRef.value!)
offsetX = arr[0]
offsetY = arr[1]
ctx.value!.globalCompositeOperation = 'destination-out'
/* 画笔粗细*/
ctx.value!.lineWidth = lineWidth
ctx.value!.beginPath()
/* 移动画笔原点*/
ctx.value!.moveTo(event.touches[0].pageX - offsetX, event.touches[0].pageY - offsetY)
}, {
passive: true
})
/** 监听鼠标移动事件 */
cancasRef.value.addEventListener("touchmove", (event) => {
/* 根据手指移动画线,使之变透明*/
ctx.value!.lineTo(event.touches[0].pageX - offsetX, event.touches[0].pageY - offsetY)
/* 填充*/
ctx.value!.stroke()
}, {
passive: true
})
/** 监听鼠标松开事件 */
cancasRef.value.addEventListener("touchend", () => {
/** (getImageData)该方法会返回canvas像素点对象 */
const pixelObj = ctx.value!.getImageData(0, 0, width, height)
/** 拿到大小用于遍历 */
const pixel = pixelObj.width * pixelObj.height
/** 记录已经刮开的像素点 */
let transparentPixels = 0
for(let i = 0; i < pixel; i++) {
/**
* 1.data属性为一个数组,每4个元素对应一个像素点
* 2.可以根据像素点的opcity值来判断这个像素点是不是透明,是不是等于0?
*/
if (pixelObj.data[i * 4 + 3] === 0) {
transparentPixels++
}
}
if(transparentPixels >= pixel * (clearZoneRadius / 100)) {
requestAnimationFrame(clear(255, width, height))
}
}, false)
}
})
</script>
<template>
<div class="container">
<div class="content" ref="contentRef">
<slot />
</div>
<canvas ref="cancasRef" />
<div class="mask" v-if="!isCanDraw"></div>
</div>
</template>
<style scoped lang="less">
.container {
position: relative;
width: max-content;
.content {
position: absolute;
z-index: -1;
}
.mask {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
}
</style>
八、借鉴
结语
到这里就告一段落了,后续会补充一下使用Vue3的TransitionGroup效果,如果有问题或者是需要改正的请帮忙指出来,谢谢!