移动端 原生 js + canvas 实现刮奖效果(适配rem布局)

效果

在这里插入图片描述

思路

  • 使用 canvas 绘制刮奖灰色遮罩部分
  • 监听 touchstart、touchmove 和 touchend 事件
  • 在事件中处理擦除效果

rem 环境

本文的 rem 布局基础为:基于宽度为 750px 的设计图,1rem = 100px

编码

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"
    />
    <title>Document</title>
    <script>
      // rem适配,基于宽度为 750px 的设计图,1rem = 100px
      ;(function (doc, win) {
        const docEl = doc.documentElement
        const isIOS = navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)
        let dpr
        dpr = isIOS ? Math.min(win.devicePixelRatio, 3) : 1
        dpr = window.top === window.self ? dpr : 1 // 被iframe引用时,禁止缩放
        dpr = 1
        const scale = 1 / dpr
        const resizeEvt =
          'orientationchange' in window ? 'orientationchange' : 'resize'
        docEl.dataset.dpr = dpr
        const metaEl = doc.createElement('meta')
        metaEl.name = 'viewport'
        metaEl.content =
          'initial-scale=' +
          scale +
          ',maximum-scale=' +
          scale +
          ', minimum-scale=' +
          scale
        docEl.firstElementChild.appendChild(metaEl)
        const recalc = function () {
          let width = docEl.clientWidth
          if (width / dpr > 750) {
            width = 750 * dpr
          }
          docEl.style.fontSize = 100 * (width / 750) + 'px'
        }
        recalc()
        if (!doc.addEventListener) return
        win.addEventListener(resizeEvt, recalc, false)
      })(document, window)
    </script>
    <style type="text/css">
      * {
        margin: 0;
        padding: 0;
      }
      html {
        font-size: 14px;
      }
      .prize_wrapper {
        width: 5rem;
        height: 2rem;
        position: relative;
        margin: 1rem auto 0;
      }
      .prize_wrapper .prize {
        position: absolute;
        width: 100%;
        height: 100%;
        text-align: center;
        line-height: 2rem;
        font-size: 0.5rem;
      }
      .prize_wrapper .prize_mask {
        position: absolute;
        width: 100%;
        height: 100%;
      }
    </style>
  </head>

  <body>
    <div class="prize_wrapper">
      <div class="prize"></div>
      <canvas class="prize_mask"></canvas>
    </div>
  </body>
  <script type="text/javascript">
    // 获取DOM对象
    let prizeWrapper = document.querySelector('.prize_wrapper')
    let prize = document.querySelector('.prize_wrapper .prize')
    prize.innerText = '一等奖'
    let prizeMask = document.querySelector('.prize_wrapper .prize_mask')
    // 获取根元素的font-size
    let rootFontSize = +window
      .getComputedStyle(document.querySelector('html'))
      .fontSize.replace(/px/, '')
    let canvasWidth = rootFontSize * 5 // 设置宽度为5rem
    let canvasHeight = rootFontSize * 2 // 设置高度为2rem

    let ctx // context 对象
    // canvas 初始化
    function init() {
      prizeMask.setAttribute('width', canvasWidth + 'px')
      prizeMask.setAttribute('height', canvasHeight + 'px')
      ctx = prizeMask.getContext('2d')
      ctx.globalAlpha = 1
      ctx.fillStyle = '#ccc'
      ctx.fillRect(0, 0, canvasWidth, canvasHeight)
      ctx.fillStyle = '#000'
      ctx.font = '20px 微软雅黑'
      ctx.textAlign = 'center'
      ctx.textBaseline = 'middle'
      ctx.fillText('刮刮奖', canvasWidth / 2, canvasHeight / 2)
    }
    init()

    // 坐标信息
    let mX, mY
    let flag = false
    prizeMask.ontouchstart = function (e) {
      flag = true
      document.querySelector('html').style.overflow = 'hidden'
      // 如果想要点击之后就开始进行擦除,下方解注释即可
      // mX = e.touches[0].pageX - prizeWrapper.offsetLeft // 使用pageX可解决有滚动条时位置错位的问题
      // mY = e.touches[0].pageY - prizeWrapper.offsetTop
      // drawArc(mX, mY)
    }
    document.body.ontouchmove = function (e) {
      if (flag == true) {
        mX = e.touches[0].pageX - prizeWrapper.offsetLeft // 使用pageX可解决有滚动条时位置错位的问题
        mY = e.touches[0].pageY - prizeWrapper.offsetTop
        drawArc(mX, mY)
      }
    }
    document.body.ontouchend = function () {
      flag = false
      document.querySelector('html').style.overflow = 'auto'
      sayPrize()
    }

    // 擦除函数
    function drawArc(x, y) {
      ctx.globalCompositeOperation = 'destination-out' //相交部分不显示
      ctx.beginPath()
      ctx.fillStyle = 'white'
      ctx.moveTo(x, y)
      ctx.arc(x, y, 10, 0, 2 * Math.PI)
      ctx.fill()
    }

    // 显示获奖信息
    function sayPrize() {
      let myImg = ctx.getImageData(0, 0, canvasWidth, canvasHeight)
      let num = 0
      let max = myImg.data.length / 4
      for (let i = 0; i < myImg.data.length; i += 4) {
        if (myImg.data[i + 3] <= 200) {
          num++
        }
      }
      if (num >= max * 0.2) {
        console.log('恭喜您,获得:' + prize.innerText)
        // 获奖后重置一下
        init()
      }
    }
  </script>
</html>
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值