對於小程式canvas在某些情況下touchmove 不能連續觸發導致的簽名不連續替代方案(企微)

story.Write(z)發表於2024-04-21

1.問題

image
微信開放社群連結

嘗試過新版canvas,在企業微信中籤名依然是依然斷觸,有問題的手機是iphoe15,系統版本以及企微版本微信版本均與簽名正常的手機一致,但是那個手機就是無法正常簽字,在微信中無論新舊canvas均能正常簽字

2.解決方案

既然canvas的touchmove觸發有問題,那麼就可以透過替代canvas的touchmove來實現,透過在canvas上覆蓋一層dom,透過這層dom的touchmove來獲取手指劃過的軌跡即可,此文章中並沒有小程式實際程式碼只是使用了h5驗證可行性的程式碼

2.1 注意點
  • 要區別手指是否連續滑動,由於點選事件觸發存在如下情況

區別手指是否連續滑動採用時間間隔判斷
觸發事件間隔小於80ms 主要用於判斷是否鬆開手指再次滑動,正常手速來說80ms,人很難在畫完一個線段後,鬆手再次畫一個線段,如果無這個處理會出現滑動一個線段之後,再次點選另一個點會把線段和新點位連線起來

沒有采取透過touchstart與touchend做一個判斷是因為touchmove並不是固定一直在start與end事件中間觸發
直接點選觸發事件順序

2.2 移動端瀏覽器體驗地址
2.2 vue2程式碼
<template>
  <div class="DomCanvasSignature">
    <div :style="{ height: height + 'px', width: width + 'px' }" class="signatureWrapper" id="signatureWrapper"
      draggable="false" @mousedown="touchstart" @mouseup="touchend" @touchstart="touchstart" @touchend="touchend"
      @touchmove="touchmove" @mousemove="touchmove">
      <canvas canvas-id="999" :height="height" :width="width - 3" class="canvas" />
    </div>
  </div>
</template>
<script>
export default {
  name: 'DomCanvasSignature',
  data () {
    return {
      height: 302,
      width: 302,

      mycanvas: null,
      previousPoint: {
        x: 0,
        y: 0
      },
      isPcStart: false,
      removeLisner: () => { }
    }
  },


  methods: {
    initSize () {
      this.width = window.innerWidth
      this.height = window.innerHeight - 300
    },
    lisner () {
      this.initSize()
      window.addEventListener('resize', this.initSize)
      return () => {
        window.removeEventListener('resize', this.initSize)
      }
    },
    touchstart () {
      this.isPcStart = true
      console.log('====start') // zdz-log
    },
    touchend () {
      this.isPcStart = false
      console.log('====end') // zdz-log

    },
    touchmove (e) {
      console.log('move', e) // zdz-log
      // 阻止滾動
      e.preventDefault()
      if (e.type === 'mousemove' && !this.isPcStart) {
        return
      }
      // 合併處理 pc 與移動端
      const changeObj = e.changedTouches && e.changedTouches[0] || e
      const current = { x: changeObj.clientX, y: changeObj.clientY, timeStamp: e.timeStamp }

      // 1.獲取元素
      // 2.獲取上下文,繪製工具箱
      let ctx = this.mycanvas.getContext('2d')
      // 3.移動畫筆
      const currentY = (current.y) - signatureWrapper.offsetTop
      // todo 改為touchstart 與end判斷 無法實現 因為move 執行存在在 start end事件之後
      let diffLarge = false
      console.log(current.timeStamp - this.previousPoint.timeStamp) // zdz-log
      // 判斷是否鬆手重新繪製
      if (this.previousPoint.timeStamp) {
        const timeDiff = current.timeStamp - this.previousPoint.timeStamp > 80
        if (timeDiff) {
          diffLarge = true
        }
      }

      const preY = diffLarge ? current.y - signatureWrapper.offsetTop : (this.previousPoint.y || current.y) - signatureWrapper.offsetTop
      const moveX = diffLarge ? current.x : this.previousPoint.x || current.x
      ctx.moveTo(moveX, preY < 0 ? 0 : preY)
      // 4.繪製直線(軌跡,繪製路徑)
      ctx.lineTo(current.x, currentY < 0 ? 0 : currentY)
      // 5.描邊
      ctx.stroke()

      this.previousPoint = current

    },


  },
  created () {
    this.removeLisner = this.lisner()
  },
  destroyed () {
    this.removeLisner()
  },
  mounted () {
    this.mycanvas = document.querySelector('canvas')
    this.signatureWrapper = document.getElementById('signatureWrapper')
  },

}
</script>

<style scoped>
.canvas {
  border: 1px solid red;
}

.signatureWrapper {
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid black;
  background-color: transparent;
}
</style>


相關文章