实现js页面拖动功能记录

背景

这几天有个需求是在页面上写多个悬浮窗,然后窗口太多,所以需要可以把窗口位置拖开的功能。

实现

然后写了一个vue3的自定义指令,实现拖动的功能

 directives: {
      moveable: {
        mounted(el, binding, vnode) {
          tsUtils.onDragEvent(({onMove, onUp, onDown}) => {
            let last = {x: 0, y: 0};
            let offset = {x: 0, y: 0};
            let isDrag = false;
            onDown(() => {
              isDrag = false;
            });
            onMove((e, curXY, lastXY, downXY) => {
              // vant pc模拟touch事件插件touch-emulator模拟的事件
              if(!e.isTrusted) return;
              const moveX = curXY.x - downXY.x + offset.x;
              const moveY = curXY.y - downXY.y + offset.y;
              last = {x: moveX, y: moveY};
              tsUtils.setStyle({
                transform: `translate(${moveX}px,${moveY}px)`
              }, {el});
              e.preventDefault();
              e.stopPropagation();
              e.returnValue = false;
              return false;
            });
            onUp((e, upXy, downXY) => {
              offset = last;
              if(!e.isTrusted) {
                const xy = e.changedTouches[0];
                upXy = {x: xy.screenX, y: xy.screenY};
              }
              isDrag = Math.abs(downXY.x - upXy.x) > 10 || Math.abs(downXY.y - upXy.y) > 10;
              if(!isDrag) {
                return;
              }
              e.preventDefault();
              e.stopPropagation();
              return false;
            });
            el.addEventListener("click", (e) => {
              if(!isDrag) {
                return;
              }
              e.preventDefault();
              e.stopPropagation();
              return false;
            }, true);
          }, {el, capture: {down: true, up: true, move: true}});
        }
      }
    },

页面跟随问题

功能是实现了,但是页面总是会跟随拖动事件而滚动,touchmove事件添加了阻止浏览器默认行为也不行。然后我就百度了半天《touchmove阻止浏览器滚动》,但是都是用的这段代码

 e.preventDefault();
 e.stopPropagation();
 e.returnValue = false;
 eturn false;

然而一点用都没有。

而且还会这样的错误

welfare_listing.html?_ijt=o4g8gice0spiph1rgjosn8gotb&_ij_reload=RELOAD_ON_SAVE:766 [Intervention] Ignored attempt to cancel a touchend event with cancelable=false, for example because scrolling is in progress and cannot be interrupted.

柳暗花明

但是这报错挺眼熟的,想起刚好这几天看过群友蒜薹炒肉加辣的《使用van-swipe-cell组件滚动报错》,发现使用touch-action: none;可以解决报错的问题。

然后我写上了

:root {
    touch-action: none;
}

ok,果然不会报错了,但是好家伙,整个页面不能滚动了。嗯?不能滚动?这不正是我要阻止页面跟随滚动的效果吗? 只要我把监听拖动事件的dom加上这属性不就好了吗?

css属性解释

此时看看MDN关于它的解释

CSS属性 touch-action 用于设置触摸屏用户如何操纵元素的区域(例如,浏览器内置的缩放功能)。
值为none:当触控事件发生在元素上时,不进行任何操作。

功能完成

给监听的dom加上css属性,大功告成,这样一个拖动的vue3自定义属性就完成了。
完整代码如下

directives: {
      moveable: {
        mounted(el, binding, vnode) {
          tsUtils.setStyle({touchAction: 'none'}, {el});
          tsUtils.onDragEvent(({onMove, onUp, onDown}) => {
            let last = {x: 0, y: 0};
            let offset = {x: 0, y: 0};
            let isDrag = false;
            onDown(() => {
              isDrag = false;
            });
            onMove((e, curXY, lastXY, downXY) => {
              // 模拟的事件
              if(!e.isTrusted) return;
              const moveX = curXY.x - downXY.x + offset.x;
              const moveY = curXY.y - downXY.y + offset.y;
              last = {x: moveX, y: moveY};
              tsUtils.setStyle({
                transform: `translate(${moveX}px,${moveY}px)`
              }, {el});
              e.returnValue = false;
              e.preventDefault();
              e.stopPropagation();
              return false;
            });
            onUp((e, upXy, downXY) => {
              offset = last;
              if(!e.isTrusted) {
                const xy = e.changedTouches[0];
                upXy = {x: xy.screenX, y: xy.screenY};
              }
              isDrag = Math.abs(downXY.x - upXy.x) > 10 || Math.abs(downXY.y - upXy.y) > 10;
              if(!isDrag) {
                return;
              }
              e.preventDefault();
              e.stopPropagation();
              return false;
            });
            el.addEventListener("click", (e) => {
              if(!isDrag) {
                return;
              }
              e.preventDefault();
              e.stopPropagation();
              return false;
            }, true);
          }, {el, capture: {down: true, up: true, move: true}});
        }
      }
    },

最后

touch-action属性的值并不仅仅只有none还有好几个属性可供使用,还有更多的功能等待我们的发掘.

通过js屏蔽浏览器滚动事件【后续】

今天(21-12-10)发现通过js是可以拦截拖动事件的,参考vantoverlay组件链接
通过查看源码发现也就是在touchmove事件上使用了

 e.preventDefault();
 e.stopPropagation();

但是我在上面已经尝试过是不起作用的。
然后查看dom发现vant的dom上的passive有些不同。

查看文档里面有条链接:使用 passive 改善的滚屏性能
所以其实最开始的报错是passive:true所导致的,只要把它设置为false就不会报错了,可以拦截浏览器的默认滚动事件了。

评论

0 / 800
全部评论()