uni-app的刮獎效果,要注意的點:
1.小程序的canvas層級問題(可用cover-view規避)
2.不能操作dom
以下是uni-app刮獎效果的代碼:
<template>
<!-- 刮獎 -->
<view v-else class="scratch">
<view class="box">
<!-- 刮獎結果圖片 -->
<image :src="domain+prize_img" class="img"></image>
<!-- 刮獎canvas容器 -->
<canvas
class="canvas-box"
canvas-id="canvas-id"
:disable-scroll="true"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
></canvas>
<!-- 刮獎前提示消息,開始刮獎後隱藏 -->
<cover-view class="tip" v-if="!toDraw">
<cover-view class="text">
<cover-view class="text-tip">你還有</cover-view>
<cover-view class="light">1</cover-view>
<cover-view class="text-tip">次刮獎機會</cover-view>
</cover-view>
<!-- 開始刮獎按鈕 -->
<cover-view class="btn" @tap="scratchStart()">
<cover-image :src="domain + '/front/img/scratch-btn-bg.png'" class="btn-img"></cover-image>
<cover-view class="text">立即刮獎</cover-view>
</cover-view>
</cover-view>
<!-- 如果後端沒有不中獎的圖,則不中獎時默認顯示 -->
<view v-if="hasDraw" class="award-box">
<text class="text">謝謝參與</text>
</view>
</view>
</view>
</template>
<script>
import Scratch from '@/utils/scratch.js'
export default {
data () {
return {
domain: getApp().globalData.baseUrl, // 服務器域名
toDraw: false, // 是否開始刮獎
showResult: false, // 是否顯示抽獎結果
isWin: true, // 是否中獎
scratchWidth: 350, // 繪製刮獎範圍寬
scratchHeight: 150, // 繪製刮獎範圍高
scratchSize: 10, // 觸手大小
scratchScale: 0.25, // 需刮開百分比
prize_img: '', // 中獎圖片
isScratching: false // 是否正在刮獎,
}
},
methods: {
initCanvas () {
// 刮獎初始化信息必須在onReady後,不然h5不支持(小程序可在onLoad執行)
new Scratch(this, {
canvasId: 'canvas-id',
width: this.scratchWidth,
height: this.scratchHeight,
size: this.scratchSize,
scale: this.scratchScale
})
},
// 請求刮獎結果
doPrizeScratchTicketFun () {
if (this.isScratching) return false
this.isScratching = true
// 請求服務器刮獎結果信息
this.$api.luckDraw
.doPrizeScratchTicket()
.then(res => {
this.prize_img = res.luckyPrize.prize_img
this.luckyPrize = res.luckyPrize
this.isWin = true
if (this.luckyPrize.is_winning === 2) this.isWin = false
this.isScratching = false
})
.catch(() => {
this.isScratching = false
})
},
// 點擊按鈕開始刮獎
scratchStart () {
this.toDraw = true
this.initCanvas()
this.doPrizeScratchTicketFun()
}
}
}
</script>
<style lang="scss">
.scratch {
width: 670upx;
height: 320upx;
background: url($baseImgUrl+"luck-draw-guajiang.png") no-repeat;
background-size: contain;
margin: 366upx auto 80upx;
padding: 29upx 25upx 21upx;
box-sizing: border-box;
position: relative;
overflow: hidden;
.box {
width: 100%;
height: 100%;
background: $subGrey;
border-radius: 20upx;
position: relative;
overflow: hidden;
.img {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
.canvas-box {
position: absolute;
top: 0;
left: 0;
width: 100.5%;
height: 100%;
border-radius: 20upx;
overflow: hidden;
}
.tip {
position: absolute;
left: 0;
right: 0;
width: 100%;
height: 100%;
z-index: 999;
text-align: center;
.text {
font-size: 30upx;
font-weight: bold;
color: $darkTextColor;
margin-top: 69upx;
.text-tip {
display: inline-block;
vertical-align: middle;
}
.light {
color: $red;
display: inline-block;
vertical-align: middle;
margin: 0 6upx !important;
}
}
.btn {
width: 360upx;
height: 82upx;
border: none;
border-radius: 41upx;
margin: 59upx auto 0;
position: relative;
background: none;
.btn-img {
border: none;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.text {
display: inline;
position: absolute;
text-align: center;
color: #fff;
font-size: 30upx;
font-weight: bold;
margin: 0;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
}
}
.award-box {
width: 100%;
height: 100%;
border-radius: 20upx;
text-align: center;
line-height: 270upx;
background: $yellow;
.text {
font-size: 40upx;
font-weight: bold;
color: $darkTextColor;
}
}
}
}
</style>
<!-- scratch.js文件的內容(參考scratch.js) -->
class Scratch {
constructor (page, opts) {
opts = opts || {}
this.page = page
this.canvasId = opts.canvasId || 'canvas'
this.width = opts.width || 340
this.height = opts.height || 150
this.maskColor = opts.maskColor || '#D2D2D2'
this.size = opts.size || 10
this.r = this.size * 2
this.area = this.r * this.r
this.scale = opts.scale || 0.3
this.totalArea = this.width * this.height
this.init()
}
init () {
this.show = false
this.clearPoints = []
this.ctx = uni.createCanvasContext(this.canvasId)
this.drawMask()
this.bindTouch()
}
drawMask () {
this.ctx.setFillStyle(this.maskColor)
this.ctx.fillRect(0, 0, this.width, this.height)
this.ctx.draw()
}
bindTouch () {
this.page.touchStart = (e) => {
this.eraser(e, true)
}
this.page.touchMove = (e) => {
this.eraser(e, false)
}
this.page.touchEnd = (e) => {
if (this.show) {
this.ctx.clearRect(0, 0, this.width, this.height)
this.ctx.draw()
}
}
}
eraser (e, bool) {
const len = this.clearPoints.length
let count = 0
const x = e.touches[0].x; const y = e.touches[0].y
const x1 = x - this.size
const y1 = y - this.size
if (bool) {
this.clearPoints.push({
x1: x1,
y1: y1,
x2: x1 + this.r,
y2: y1 + this.r
})
}
for (const item of this.clearPoints) {
if (item.x1 > x || item.y1 > y || item.x2 < x || item.y2 < y) {
count++
} else {
break
}
}
if (len === count) {
this.clearPoints.push({
x1: x1,
y1: y1,
x2: x1 + this.r,
y2: y1 + this.r
})
}
if (len && this.r * this.r * len > this.scale * this.totalArea) {
this.show = true
}
this.clearArcFun(x, y, this.r, this.ctx)
this.ctx.draw(true)
}
clearArcFun (x, y, r, ctx) {
let stepClear = 1
clearArc(x, y, r)
function clearArc (x, y, radius) {
const calcWidth = radius - stepClear
const calcHeight = Math.sqrt(radius * radius - calcWidth * calcWidth)
const posX = x - calcWidth
const posY = y - calcHeight
const widthX = 2 * calcWidth
const heightY = 2 * calcHeight
if (stepClear <= radius) {
ctx.clearRect(posX, posY, widthX, heightY)
stepClear += 1
clearArc(x, y, radius)
}
}
}
}
export default Scratch