DomEvent.Pointer
# 概述
DomEvent.Pointer
是Leaflet 框架中用于 统一处理指针事件(Pointer Events) 的核心模块,目的是将不同浏览器(尤其是旧版 IE)的指针事件(如 MSPointer
)和标准指针事件(PointerEvent
)抽象为统一的触摸事件(touchstart
/touchmove
等),以便在不同浏览器中实现一致的触摸行为。
# 源码分析
# 源码实现如下
DomEvent.Pointer
源码实现如下:
var POINTER_DOWN = Browser.msPointer ? "MSPointerDown" : "pointerdown";
var POINTER_MOVE = Browser.msPointer ? "MSPointerMove" : "pointermove";
var POINTER_UP = Browser.msPointer ? "MSPointerUp" : "pointerup";
var POINTER_CANCEL = Browser.msPointer ? "MSPointerCancel" : "pointercancel";
var pEvent = {
touchstart: POINTER_DOWN,
touchmove: POINTER_MOVE,
touchend: POINTER_UP,
touchcancel: POINTER_CANCEL,
};
var handle = {
touchstart: _onPointerStart,
touchmove: _handlePointer,
touchend: _handlePointer,
touchcancel: _handlePointer,
};
var _pointers = {};
var _pointerDocListener = false;
export function addPointerListener(obj, type, handler) {
if (type === "touchstart") {
_addPointerDocListener();
}
if (!handle[type]) {
console.warn("wrong event specified:", type);
return falseFn;
}
handler = handle[type].bind(this, handler);
obj.addEventListener(pEvent[type], handler, false);
return handler;
}
export function removePointerListener(obj, type, handler) {
if (!pEvent[type]) {
console.warn("wrong event specified:", type);
return;
}
obj.removeEventListener(pEvent[type], handler, false);
}
function _globalPointerDown(e) {
_pointers[e.pointerId] = e;
}
function _globalPointerMove(e) {
if (_pointers[e.pointerId]) {
_pointers[e.pointerId] = e;
}
}
function _globalPointerUp(e) {
delete _pointers[e.pointerId];
}
function _addPointerDocListener() {
if (!_pointerDocListener) {
document.addEventListener(POINTER_DOWN, _globalPointerDown, true);
document.addEventListener(POINTER_MOVE, _globalPointerMove, true);
document.addEventListener(POINTER_UP, _globalPointerUp, true);
document.addEventListener(POINTER_CANCEL, _globalPointerUp, true);
_pointerDocListener = true;
}
}
function _handlePointer(handler, e) {
if (e.pointerType === (e.MSPOINTER_TYPE_MOUSE || "mouse")) {
return;
}
e.touches = [];
for (var i in _pointers) {
e.touches.push(_pointers[i]);
}
e.changedTouches = [e];
handler(e);
}
function _onPointerStart(handler, e) {
if (e.MSPOINTER_TYPE_TOUCH && e.pointerType === e.MSPOINTER_TYPE_TOUCH) {
DomEvent.preventDefault(e);
}
_handlePointer(handler, e);
}
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# 源码详解
- 变量定义与兼容性处理
var POINTER_DOWN = Browser.msPointer ? "MSPointerDown" : "pointerdown";
var POINTER_MOVE = Browser.msPointer ? "MSPointerMove" : "pointermove";
var POINTER_UP = Browser.msPointer ? "MSPointerUp" : "pointerup";
var POINTER_CANCEL = Browser.msPointer ? "MSPointerCancel" : "pointercancel";
1
2
3
4
2
3
4
- 目的:根据浏览器是否支持
MSPointer
(旧版 IE)选择对应的事件名称,实现兼容性适配。 - 关键点:Browser.msPointer
是 Leaflet 内部的浏览器特性检测标志,用于判断是否为旧版 IE。
- 事件类型映射
var pEvent = {
touchstart: POINTER_DOWN,
touchmove: POINTER_MOVE,
touchend: POINTER_UP,
touchcancel: POINTER_CANCEL,
};
var handle = {
touchstart: _onPointerStart,
touchmove: _handlePointer,
touchend: _handlePointer,
touchcancel: _handlePointer,
};
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
目的:将标准触摸事件类型(如
touchstart
)映射到实际的指针事件(如pointerdown
)和对应的处理函数。关键点:
pEvent
:事件名映射表。handle
:事件处理函数映射表,统一入口处理不同事件。
- 指针状态管理
var _pointers = {}; // 存储当前活动的指针(如多个触控点)
var _pointerDocListener = false; // 标记是否已全局监听文档级指针事件
1
2
2
- 目的:跟踪所有活动的指针(例如多点触控场景中的每个手指),确保事件处理的连续性。
4. 添加/移除事件监听器
export function addPointerListener(obj, type, handler) {
if (type === "touchstart") {
_addPointerDocListener();
}
handler = handle[type].bind(this, handler);
obj.addEventListener(pEvent[type], handler, false);
return handler;
}
export function removePointerListener(obj, type, handler) {
obj.removeEventListener(pEvent[type], handler, false);
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
逻辑流程:
- 当添加
touchstart
监听时,触发_addPointerDocListener()
,全局监听文档级指针事件。 - 将用户传入的
handler
绑定到对应的事件处理函数(如_onPointerStart
)。 - 使用
addEventListener
注册事件。
- 当添加
关键点:
bind(this, handler)
确保处理函数中的this
指向正确。- 返回包装后的
handler
,便于后续移除监听。
- 全局指针事件监听
function _addPointerDocListener() {
if (!_pointerDocListener) {
document.addEventListener(POINTER_DOWN, _globalPointerDown, true);
document.addEventListener(POINTER_MOVE, _globalPointerMove, true);
document.addEventListener(POINTER_UP, _globalPointerUp, true);
document.addEventListener(POINTER_CANCEL, _globalPointerUp, true);
_pointerDocListener = true;
}
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
- 目的:在文档级别捕获所有指针事件,确保即使指针移动到元素外部,仍能跟踪状态。
- 关键点:
- 使用 捕获阶段(true) 监听事件,确保优先处理。
_globalPointerDown
/_globalPointerMove
/_globalPointerUp
维护_pointers
对象。
- 指针状态更新
function _globalPointerDown(e) {
_pointers[e.pointerId] = e; // 记录指针按下时的状态
}
function _globalPointerMove(e) {
if (_pointers[e.pointerId]) {
_pointers[e.pointerId] = e; // 更新指针移动后的状态
}
}
function _globalPointerUp(e) {
delete _pointers[e.pointerId]; // 删除已释放的指针
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
- 目的:维护
_pointers
对象,记录所有活动指针的实时数据。
- 事件处理与转换
function _handlePointer(handler, e) {
if (e.pointerType === (e.MSPOINTER_TYPE_MOUSE || "mouse")) {
return; // 忽略鼠标事件
}
// 将指针事件转换为触摸事件结构
e.touches = [];
for (var i in _pointers) {
e.touches.push(_pointers[i]);
}
e.changedTouches = [e]; // 当前触发事件的指针
handler(e); // 调用用户的事件处理函数
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
- 关键点:
- 过滤鼠标事件:仅处理触控笔或手指触控。
- 模拟触摸事件结构:
e.touches
: 所有当前活动的触控点。e.changedTouches
: 当前发生变化的触控点(例如本次事件的触控点)。
- 触摸开始事件处理
function _onPointerStart(handler, e) {
if (e.MSPOINTER_TYPE_TOUCH && e.pointerType === e.MSPOINTER_TYPE_TOUCH) {
DomEvent.preventDefault(e); // 阻止默认行为(如滚动)
}
_handlePointer(handler, e);
}
1
2
3
4
5
6
2
3
4
5
6
- 目的:在触摸开始时,阻止浏览器默认行为(如页面滚动),确保 Leaflet 能完全控制交互。
- 关键点:
- 仅对触摸类型(非鼠标/触控笔)阻止默认行为。
# 总结
核心逻辑: Leaflet 通过抽象指针事件,将其转换为统一的触摸事件,解决了不同浏览器(尤其是旧版 IE)的兼容性问题,并支持多点触控操作。
设计亮点:
- 全局指针跟踪:通过文档级监听维护所有指针状态。
- 事件结构模拟:将
PointerEvent
转换为类似TouchEvent
的结构,统一上层逻辑。 - 性能优化:仅在需要时(如首次
touchstart
)添加全局监听,避免资源浪费。
适用场景: 地图拖拽、缩放、绘制等需要处理复杂触控交互的功能。
编辑 (opens new window)
上次更新: 2025/03/21, 01:49:37