小程序上如何使用 canvas 实现刮刮乐效果?

如果要在网页 h5 上实现刮刮乐效果,其实很多人都知道原理非常简单,用一个 canvas 盖在结果内容上层,然后给 canvas 绑定 ontouchmove 事件,触发后使用 context.clearRect(x, y, width, height);清除 canvas 上的点击位置中 x, y 位置的一个 10px * 10px 矩形即可实现刮刮乐效果。但如果在小程序呢?

在小程序中实现刮刮乐效果

在小程序中实现刮刮乐的原理也是相似,但由于 API 的差异,写法有所不同。这里以微信小程序 CanvasContext API,使用 wepy 作为开发框架为例(当然使用微信小程序新版 Canvas API 也可以,更像 h5 中使用 canvas):

<style lang="less">
.ggl {
  width: 394rpx;
  height: 110rpx;
  &__content {
    width: 394rpx;
    height: 110rpx;
    position: relative;
    image {
      width: 100%;
      height: 100%;
    }
  }
  .canvas {
    position: absolute;
    top: 10rpx;
    left: 9rpx;
    width: 374rpx !important;
    height: 92rpx !important;
  }
}
</style>
<template>
    <view class="ggl">
        <view class="ggl__content">
            <image src="../images/ggl/my-result.png" /><!--这里是结果显示的内容,下面的 canvas 会盖住结果-->
        </view>
        <canvas class="canvas" style="width: {{width}}px; height: {{height}}px;" canvas-id="canvas" @touchmove="touchMove" />
    </view>
</template>

<script>
import wepy from 'wepy'
export default class Ggl extends wepy.page {
    config = {
        navigationBarTitleText: '刮刮乐'
    }
    data = {
        gglCoverImgUrl: '../images/ggl/my-cover.png', // 刮刮乐长什么样子,由这图片定
        context: wepy.createCanvasContext('canvas'),
        scale: 750 / wepy.getSystemInfoSync().windowWidth, // 注意:canvas 设置的 px 单位转 rpx 单位时,需要用到比例
        width: 374,
        height: 92
    }
    computed = {}
    methods = {
        touchMove (e) {
            if (e && e.touches[0]) {
                const p = e.touches[0]
                const { context } = this
                context.clearRect(p.x - 5, p.y - 5, 10, 10) // 关键代码:每次清一个方块,实现刮刮乐效果
                context.draw(true) // 关键代码:这里使用 true 参数意思是保存上一次对画布的操作,如果是 false 画布会被清空哦
            }
        }
    }
    events = {}
    onLoad (option) {
        this.initCanvas()
    }
    initCanvas () { // 先在 canvas 上画上马赛克图片
        const { scale, context, width, height, gglCoverImgUrl } = this
        const realWidth = width / scale  // 注意 px -> rpx 需要按比例换算
        const realHeight = height / scale // 注意 px -> rpx 需要按比例换算
        context.drawImage(gglCoverImgUrl, 0, 0, realWidth, realHeight)
        context.draw()
    }
}
</script>