移动端图片单指移动,双指放大缩小实现//touchmove的时候不让body滚动

移动端图片单指移动,双指放大缩小实现//touchmove的时候不让body滚动

思路1:移动放大缩小操作都直接放到图片上面

  1. 图片的放大缩小使用transform的scale属性操作,移动使用transform的translate属性操作,也可以使用matrix属性做2D转换,这个有六个值,14表示xy轴的放大倍数,56表示xy轴的偏移量,23表示度数,这里不操作旋转可以写成默认的0
  2. 根据touches获取当前触摸位置,touches是一个数组,表示当前有几个手指触摸屏幕,可以判断一下,是单指的时候在ontouchstart时记录手指触摸的位置,然后在ontouchmove时计算现在手指的移动位置,计算两者的偏移量,在图片上使用transform属性的matrix或translate控制图片做出移动(可以在ontouchend的时候判断一下要不要移动回原位置,或者在放大一倍的时候就让回原位置,或者在ontouchmove的时候限制一下图片的移动范围)
  3. 根据touches有两个值的时候做出判断,让图片根据手势放大缩小.在ontouchstart的时候进行根据两个手指的位置计算出来现在两个手指的距离(勾股定理)记录下来,在ontouchmove的时候计算当前手指的距离,拿这个距离和之前start时记录的距离相除,根据这个值来确定当前放大倍数.然后在图片上面做出操作,控制放大缩小程度.(这里会有个几个坑)
    3.1 因为将放大和移动都放到图片dom上,所以两个属性之间会有一些相互影响,这个需要处理下
    3.2 在放大的时候需要确定放大的中心,这时候有两种确定中心的情况,第一种:在ontouchstart的时候计算双指的中心位置,把这个确定为transform-origin,第二种是在ontouchmove的时候动态计算双指之间的位置在原始图片上的坐标,这里计算复杂一些,需要考虑到图片放大倍数和偏移量,需要通过记录下来的图片位置,和当前放大缩小的倍数计算当前我们看到的展示图片,和原始图片倍数是1,偏移量是0的时候的对应坐标,这里计算好对应的坐标才好计算下一步
    3.3 在确定对应坐标的情况下,因为transform-origin改变在放大倍数不为1的时候会导致图片发生移动,这里需要做一下处理,因为要保证图片在移动的时候不能跳跃式移动,在修改transform-origin的时候我们需要根据当前放大倍数和现在transform-origin-原始transform-origin之间的偏移量来计算这个图片是会上下左右移动多少位置,需要在修改transform-origin的时候同步修改一下translate偏移量,让图片不会因为origin修改而发生位置改变
    3.4 在确定中心之后就只需要通过计算双指之间的距离计算放大缩小倍数了
    ps:这里也可以使用position来确定移动位置,不过同样需要计算

思路2:因为在scale为1的时候修改transform-origin不会导致图片的偏移,所以可以将放大的操作放到ing的父元素上面,让父元素占满屏幕,让图片只做位置移动

  1. 同样的三个方法:ontouchstart/ontouchmove/ontouchend这三个,在单指触摸的时候通过css控制图片dom移动,在放大的时候控制图片父元素放大,这样就可以保证放大的不影响图片,而且不怎么用计算当前放大的中心在哪
  2. 和上面相同的需要在start记录位置,然后在move的时候记录移动的大小,计算移动的大小,修改位置
  3. 双指放大缩小也是一样

下面是根据方法二写的例子,在react项目中的,其他项目也可以只要事件能挂上去就可以,怕覆盖原先事件可以使用监听,不过一般有这个事件的都有这个功能了,都不做这些了
看项目需求
这里的例子可以去闲鱼里搜闪回有品官方店里面的验货报告里面查看

// 在移动端如果想要在touchmove的时候不让body也滚动,在弹窗的最外层阻止事件冒泡就可以了
	const box: any = document.getElementsByClassName('ant-image-preview-wrap')
      box[0].ontouchmove = (e: any) => {
        e.preventDefault()
        e.stopPropagation()
        return false
      }
useEffect(() => {
    if (visible) {
      //  这里将触摸事件绑定到图片的父元素上面
      const imgBoxDom: any = document.getElementsByClassName('ant-image-preview-img-wrapper')
      // 记录move前的图片位置,在ontouchesstart和ontouchesmove有使用到
      let dingwei: { x: number; y: number } = { x: 0, y: 0 }
      // 计算双指之间的距离
      const getDistance = (start: { x: number; y: number }, stop: { x: number; y: number }) => {
        return Math.sqrt(Math.pow(stop.x - start.x, 2) + Math.pow(stop.y - start.y, 2))
      }
      const distance = { start: 1, stop: 1 }
      // 默认的放大倍数(即时)
      let scaleXY = 1
      // 默认放大倍数(双指操作结束)
      let scale = 1
      // 图片开始移动位置
      let trans: any
      // 是不是单个手指触摸(判断防止因为在触发双指时,两个手指都移动导致weizhi字段记录的坐标和双指不同时离开屏幕导致有触发单指的情况,这种情况导致的图片位置跳动)
      let isOne = true
      imgBoxDom[0].ontouchstart = (e: any) => {
        e.preventDefault()
        if (e.touches.length == 1) {
          // 是单指触摸,保存一下现在的位置
          isOne = true
          dingwei = { x: e.changedTouches[0].pageX, y: e.changedTouches[0].pageY }
          trans = e.target.style.transform
          if (trans.indexOf('matrix') == 0) {
            trans = trans.split('(')
            trans = trans[1].split(',')
          } else {
            trans = [1, 0, 0, 1, 0, 0]
          }
        } else if (e.touches.length == 2) {
          // 双指就保存一下现在的双指距离
          isOne = false
          distance.start = getDistance(
            {
              x: e.touches[0].screenX,
              y: e.touches[0].screenY,
            },
            {
              x: e.touches[1].screenX,
              y: e.touches[1].screenY,
            }
          )
        }
      }
      imgBoxDom[0].ontouchmove = (e: any) => {
        e.preventDefault()
        e.stopPropagation()
        if (e.touches.length == 1) {
          // 开始移动,防止双指放大之后离开一指导致图片跳动,更新一下图片位置
          if (!isOne) {
            isOne = true
            dingwei = { x: e.changedTouches[0].pageX, y: e.changedTouches[0].pageY }
          }
          // 计算现在的单指移动位置
          const moveX = e.changedTouches[0].pageX - dingwei.x
          const moveY = e.changedTouches[0].pageY - dingwei.y
          // 使用scaleXY是因为有可能父元素已经被放大缩小过,这时候移动的大小就不是显示的大小了,需要计算一下实际距离
          const style = `transform:matrix(1,0,0,1,${parseFloat(trans[4]) + moveX / scaleXY},${
            parseFloat(trans[5]) + moveY / scaleXY
          });transition:transform 0s;`
          e.target.style = style
        } else if (e.touches.length == 2) {
          // 计算一下现在放大倍数
          distance.stop = getDistance(
            {
              x: e.touches[0].screenX,
              y: e.touches[0].screenY,
            },
            {
              x: e.touches[1].screenX,
              y: e.touches[1].screenY,
            }
          )
          const big = distance.stop / distance.start
          // 最大三倍,最小1倍,只需要修改倍数,位移是不需要修改的,不过在放大的时候图片是可以以双指中心为origin放大的,很迷
          scaleXY = big - 1 + scale
          scaleXY = scaleXY < 1 ? 1 : scaleXY
          scaleXY = scaleXY > 3 ? 3 : scaleXY
          const style = `transform:matrix(${scaleXY},0,0,${scaleXY},0,0);transition:transform 0s;`
          imgBoxDom[0].style = style
        }
      }
      imgBoxDom[0].ontouchend = (e: any) => {
        e.preventDefault()
        e.stopPropagation()
        scale = scaleXY
      }
      // 底下这里可以不做判断,这里是因为之前使用的antd的组件,直接在切换图片上面添加一个事件,切换图片让图片大小位置还原
      const btnDomLeft: any = document.getElementsByClassName('ant-image-preview-switch-left')
      const btnDomImg: any = document.getElementsByClassName('ant-image-preview-img')
      btnDomLeft[0].addEventListener('click', () => {
        btnDomImg[0].style = 'transform:matrix(1,0,0,1,0,0);transition:transform 0s;'
        imgBoxDom[0].style = `transform:matrix(1,0,0,1,0,0);transition:transform 0s;`
      })
      const btnDomRight: any = document.getElementsByClassName('ant-image-preview-switch-right')
      btnDomRight[0].addEventListener('click', () => {
        btnDomImg[0].style = 'transform:matrix(1,0,0,1,0,0);transition:transform 0s;'
        imgBoxDom[0].style = `transform:matrix(1,0,0,1,0,0);transition:transform 0s;`
      })
    }
  }, [visible])

在这里插入图片描述

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Vue3中实现移动端图片双指控制放大缩小和拖动,可以利用Vue官方提供的指令v-touch,结合自定义指令和事件处理进行实现。 首先,在需要实现双指控制的图片元素上,使用v-touch指令绑定一个Touch事件。 ```html <template> <div> <img src="imageSrc" v-touch="handleTouch"> </div> </template> ``` 然后,在Vue的setup函数中,定义handleTouch方法来处理触摸事件。可以通过event.touches获取触摸点信息,并根据触摸点的个数来判断是拖动还是放大缩小操作。 ```javascript <script> import { ref } from 'vue'; export default { setup() { const imageSrc = ref('path/to/image'); const handleTouch = (event) => { const touches = event.touches; if (touches.length === 2) { // 双指操作:放大缩小 // 获取两个触摸点的距离 const distance = Math.hypot(touches[0].clientX - touches[1].clientX, touches[0].clientY - touches[1].clientY); // 根据距离放大缩小图片 zoomImage(distance); } else if (touches.length === 1) { // 单指操作:拖动 // 记录当前触摸点的坐标 const touch = touches[0]; const startX = touch.clientX; const startY = touch.clientY; // 监听触摸点的移动 document.addEventListener('touchmove', handleMove); // 监听触摸点的离开 document.addEventListener('touchend', handleEnd); // 处理移动事件 function handleMove(event) { event.preventDefault(); // 计算偏移量 const offsetX = event.touches[0].clientX - startX; const offsetY = event.touches[0].clientY - startY; // 根据偏移量拖动图片 dragImage(offsetX, offsetY); } // 处理离开事件 function handleEnd() { // 移除事件监听 document.removeEventListener('touchmove', handleMove); document.removeEventListener('touchend', handleEnd); } } }; // 放大缩小图片 const zoomImage = (distance) => { // 实现放大缩小操作 }; // 拖动图片 const dragImage = (offsetX, offsetY) => { // 实现拖动操作 }; return { imageSrc, handleTouch, zoomImage, dragImage }; } }; </script> ``` 在zoomImage和dragImage方法中,根据传入的参数来实现图片放大缩小和拖动操作。具体实现方式可以根据项目需求选择。 需要注意的是,由于涉及到与DOM直接交互,所以在Vue3的Composition API中,需要使用document.addEventListener和document.removeEventListener来监听和移除事件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值