微信小程序单指拖拽和双指缩放旋转

小程序单指拖拽和双指操作是一个比较常用的功能,效果如下图
在这里插入图片描述

  • 实现这三个功能,主要用三个触摸事件touchstarttouchmovetouchend
  • <view style="height: 100vh; width: 100vw">
      <image
        src="..."
        style="transform: translate({{translateX}}px, {{translateY}}px) scale({{scale}}) rotate({{rotate}}deg);"
        catch:touchstart="touchStart"
        catch:touchmove="touchMove"
        catch:touchend="touchEnd"
      />
    </view>
    
    • 用了以下变量
    • Page({
        data: {
          translateX: 0, // 位移x坐标 单位px
          translateY: 0, // 位移y坐标 单位px
          distance: 0, // 双指接触点距离
          scale: 1, // 缩放倍数
          rotate: 0, // 旋转角度
          oldRotate: 0, // 上一次旋转停止后的角度
          startMove: { // 起始位移距离
            x: 0,
            y: 0,
          },
          startTouches: [] // 起始点touch数组
        },
      })
      

      单指拖拽

      • 单指拖拽比较简单,只需要记录移动的点坐标,然后减去起始点坐标,就可以求出针对页面的移动距离
      • touchstart
      • touchStart(e) {
          const touches = e.touches
          const { translateX, translateY } = this.data
          const { pageX, pageY } = touches[0]
          this.data.startMove = {
            x: pageX - translateX,
            y: pageY - translateY
          }
          this.data.startTouches = touches
        },
        
        • touchmove
        • touchMove(e) {
            const touches = e.touches
            const { pageX: onePageX, pageY: onePageY } = touches[0]
            const { startMove } = this.data
            this.setData({
              translateX: onePageX - startMove.x,
              translateY: onePageY - startMove.y
            })
          }
          

          双指缩放

          • 双指缩放的原理是根据两点坐标求出距离(勾股定理),然后在用移动坐标的距离比就可以求出缩放倍数
          • touchmove
      • touchMove(e) {
          const touches = e.touches
          const { pageX: onePageX, pageY: onePageY } = touches[0]
          const { startMove, scale, distance: oldDistance, startTouches } = this.data
          if (touches.length === 2 && startTouches.length === 2) {
          	// 双指缩放
            const { pageX: twoPageX, pageY: twoPageY } = touches[1]
            // 求出当前双指距离
            const distance = Math.sqrt((twoPageX - onePageX) ** 2 + (twoPageY - onePageY) ** 2)
            this.data.distance = distance
            this.setData({
              scale: scale * (distance / (oldDistance || distance))
            })
          } else if (startTouches.length !== 2) {
          	// 单指拖拽
            this.setData({
              translateX: onePageX - startMove.x,
              translateY: onePageY - startMove.y
            })
          }
        }
        

        startTouches.length !== 2这个判断的原因是防止图片跳动,因为如果你两个手指触摸,然后离开一个手指,我是禁止拖拽的,只有双指都离开后再次触摸才能单指拖拽
        双指旋转
        双指旋转的原理是根据三角函数求出起始点的角度,然后再求出移动坐标的角度,相减然后加上上一次旋转的角度就等于你当前所需的选择角度
        touchmove

        touchMove(e) {
          const touches = e.touches
          const { pageX: onePageX, pageY: onePageY } = touches[0]
          const { startMove, scale, distance: oldDistance, startTouches, oldRotate } = this.data
          if (touches.length === 2 && startTouches.length === 2) {
            const { pageX: twoPageX, pageY: twoPageY } = touches[1]
            const distance = Math.sqrt((twoPageX - onePageX) ** 2 + (twoPageY - onePageY) ** 2)
        +   let rotate = this.getAngle(touches[0], touches[1]) - this.getAngle(startTouches[0], startTouches[1]) + oldRotate
            // 如果大于360度,就减去360
        +   rotate = rotate > 360 ? rotate - 360 : rotate
            this.data.distance = distance
            this.setData({
              scale: scale * (distance / (oldDistance || distance)),
        +     rotate
            })
          } else if (startTouches.length !== 2) {
            this.setData({
              translateX: onePageX - startMove.x,
              translateY: onePageY - startMove.y
            })
          }
        },
        
        
        • getAngle
        //getAngle
        getAngle(p1, p2) {
          const x = p1.pageX - p2.pageX
          const y = p1.pageY- p2.pageY
          return Math.atan2(y, x) * 180 / Math.PI
        }
        
        • touchend
        • touchEnd() {
            // 保存当前旋转角度
            this.data.oldRotate = this.data.rotate
          },
          

          总结

          • 代码片段https://developers.weixin.qq.com/s/0nS1tImU7Rs5
          • H5原理一致,只需改一下语法即可
          • 我这个只是基础版本,如果需要一些边界控制和还一些需求的限制,计算据边框距离即可,也可以用小程序的boundingClientRectAPI
THE END