效果圖如下:

以下代碼僅為刮刮卡部分:
<template>
<div class="scratch-card">
<div :style="computedBgStyle" class="scratch-card-bg">
<div class="canvas-wrap">
<div class="prize-wrap">
<h2 class="prize-item">一等獎</h2>
<p class="prize-item">新秀麗行李箱新秀麗行李箱新秀麗行李箱新秀麗行李箱</p>
</div>
<canvas id="canvasMask" class="canvas"></canvas>
</div>
</div>
</div>
</template>
<script>
import { CTX, VERSION } from '@/constants/context'
export default {
props: {
config: {
type: Object,
required: true
}
},
data() {
return {
canvas: null,
context: null,
width: 450, // canvas寬度
height: 250, // canvas高度
scaleRatio: 1 // 縮放比例
}
},
computed: {
computedBgStyle() {
if(this.config.turnTableImg) {
return {
'backgroundImage': `url(${this.config.turnTableImg})`
}
}else {
return {
'backgroundImage': `url(${CTX}/static/img/scratch-card/prize-bg.png?v=${VERSION})`
}
}
}
},
mounted() {
this.$nextTick(() => {
const clientW = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
this.scaleRatio = clientW / 750
this.canvas = document.getElementById('canvasMask')
this.canvas.width = this.width * this.scaleRatio
this.canvas.height = this.height * this.scaleRatio
this.context = this.canvas.getContext('2d')
// 畫灰色塗層圖片
const coatImg = new Image()
coatImg.src = `${CTX}/static/img/scratch-card/coat-img.png?v=${VERSION}`
coatImg.addEventListener('load', () => {
this.context.drawImage(coatImg, 0, 0, this.canvas.width, this.canvas.height)
// 啟用了globalCompositeOperation= = 'destination-out' 之后,在 canvs 上繪制的新的圖形,就會使得之前的 mask 變成透明的,從而顯示出背景圖片來。
this.context.globalCompositeOperation = 'destination-out'
// 監聽touchmove
this.canvas.addEventListener('touchmove', this.moveHandler)
})
})
},
methods: {// 手指划過 畫圓
moveHandler(event) {
let context = this.context
event.preventDefault()
context.beginPath()
// 根據某個點在canvas上畫圓
// x 坐標和 y 坐標 兩個坐標是觸摸點的坐標而不是畫圓的圓心
// 圓心通過計算得出
var canvasRect = this.canvas.getBoundingClientRect()
context.arc(event.touches[0].pageX - canvasRect.left, event.touches[0].pageY - canvasRect.top, 20 * this.scaleRatio, 0, Math.PI * 2)
context.closePath()
context.fillStyle = '#dddddd'
context.fill()
this.checkComplete()
},
// 檢查完成度,判斷是否完成刮獎 點數大於80%
checkComplete() {
var imgData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height)
var pxData = imgData.data // 獲取字節數據
var len = pxData.length // 獲取字節長度
var count = 0 // 記錄透明點的個數
// 主要的思想是 一個像素由四個數據組成,每個數據分別是 rgba() 所以第四個數據 a 表示alpha透明度
for (var i = 0; i < len; i += 4) {
var alpha = pxData[i + 3] // 獲取每個像素的透明度
if (alpha < 10) {
// 透明度小於10
count++
}
}
var percent = count / (len / 4) // 計算百分比
// 如果百分比大於0.8 則表示成功
if (percent >= 0.8) {
this.showResult()
}
},
// 顯示刮獎結果
showResult() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height)
alert('刮獎成功')
}
}
}
</script>
<style lang="scss" scoped>
.scratch-card {
width: px2rem(690px);
height: px2rem(546px);
margin: 0 auto;
position: relative;
&-bg {
width: 100%;
height: 100%;
background-size: 100% 100%;
padding-top: px2rem(204px);
}
}
.canvas-wrap {
width: px2rem(504px);
height: px2rem(300px);
background-image: img-url('../../assets/img/scratch-card/bg-scratch.png');
background-size: 100% 100%;
margin: 0 auto;
position: relative;
color: #fff;
text-align: center;
.prize-wrap {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
padding: 0 px2rem(30px);
}
.prize-item {
@include font-dpr(30px);
@include lh-dpr(42px);
font-weight: 600;
&:first-child {
margin-bottom: px2rem(16px);
}
}
.canvas {
background-size: 100% 100%;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
}
</style>
