实现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是可以拦截拖动事件的,参考vant
的overlay
组件链接
通过查看源码发现也就是在touchmove
事件上使用了
e.preventDefault();
e.stopPropagation();
但是我在上面已经尝试过是不起作用的。
然后查看dom发现vant
的dom上的passive
有些不同。
查看文档里面有条链接:使用 passive 改善的滚屏性能
所以其实最开始的报错是passive:true
所导致的,只要把它设置为false
就不会报错了,可以拦截浏览器的默认滚动事件了。
评论