跨平台的拖拽

crossvent:封装跨平台事件绑定的库

跨平台的触摸事件

利用crossvent封装绑定跨平台拖拽事件的方法touchy,以便支持移动端和pc端

根据环境获取的触摸事件名,用懒加载的方式替换掉touchy函数,之后绑定事件不需要再判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import { add as addEvent } from 'crossvent';

let touchy = function (el, type, fn) {
const touch = {
mouseup: 'touchend',
mousedown: 'touchstart',
mousemove: 'touchmove'
};
const pointers = {
mouseup: 'pointerup',
mousedown: 'pointerdown',
mousemove: 'pointermove'
};
const microsoft = {
mouseup: 'MSPointerUp',
mousedown: 'MSPointerDown',
mousemove: 'MSPointerMove'
};
if (window.navigator.pointerEnabled) {
touchy = function (el, type, fn) {
addEvent(el, pointers[type], fn);
};
} else if (window.navigator.msPointerEnabled) {
touchy = function (el, type, fn) {
addEvent(el, microsoft[type], fn);
};
} else {
touchy = function (el, type, fn) {
addEvent(el, touch[type], fn);
addEvent(el, type, fn);
};
}

touchy(el, type, fn);
};

初始化事件

1
2
3
4
5
6
7
8
9
10
11
12
13
const documentElement = document.documentElement;

function draggable (dom) {
initEvents();

function initEvents () {
touchy(dom, 'mousedown', mousedown);
touchy(documentElement, 'mousemove', mousemove);
touchy(documentElement, 'mouseup', mouseup);
}

//...
}

这里在documentElement上绑定mousemovemouseup事件,是因为避免渲染速度低于拖拽速度,导致鼠标超出拖拽范围而无法拖拽和释放

这里提一下绑定事件时有一个passive选项,具体可以查看 使用 passive 改善的滚屏性能,可以设置为false,使事件回调可以使用e.preventDefault禁用默认的行为

mousedown

1
2
3
4
5
6
function mousedown (e) {
active = true;
e = getEventHost(e);
_moveX = e.clientX;
_moveY = e.clientY;
}

documentElement上绑定mousemovemouseup事件,所以需要标记是否是处于拖拽状态,然后mousemovemouseup中判断如果不是拖拽状态则跳出函数。

为了适配移动端(touchEvent没有clientXclientYxy),引出了getEventHost

1
2
3
4
5
6
7
8
9
function getEventHost (e) {
if (e.targetTouches && e.targetTouches.length) {
return e.targetTouches[0];
}
if (e.changedTouches && e.changedTouches.length) {
return e.changedTouches[0];
}
return e;
}

我们看看touch、targetTouches、changedTouches的定义

  • touches: 当前屏幕上所有触摸点的列表
  • targetTouches: 当前对象上所有触摸点的列表
  • changedTouches: 涉及当前(引发)事件的触摸点的列表

mousemove

这里实现的是可拖拽范围为document的可视区域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function mousemove (e) {
if (!active) { // 如果不是出于拖拽状态直接放回
return;
}
e = getEventHost(e);
const { x, y, height, width } = dom.getBoundingClientRect();
const curLeft = x + e.clientX - _moveX;
const curTop = y + e.clientY - _moveY;

dom.style.left = `${clamp(curLeft, 0, documentElement.clientWidth - width - 1)}px`;
dom.style.top = `${clamp(curTop, 0, documentElement.clientHeight - height)}px`;
_moveX = e.clientX;
_moveY = e.clientY;
}

// 将value限制在[min, max]之间
function clamp (value, min, max) {
return Math.min(Math.max(value, min), max);
}

mouseup

1
2
3
function mouseup () {
active = false;
}

总结

我们可以自定义mousemove的行为,但整体的框架不会变

作者

Liang

发布于

2021-09-12

更新于

2021-12-24

许可协议


评论