Jinuss's blog Jinuss's blog
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 《Vue》
    • 《React》
    • 《Git》
    • JS设计模式总结
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

东流

前端可视化
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 《Vue》
    • 《React》
    • 《Git》
    • JS设计模式总结
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 框架

  • core模块

  • dom模块

    • DomEvent
    • DomUtil
    • DomEvent.Pointer
      • 概述
      • 源码分析
        • 源码实现如下
        • 源码详解
      • 总结
    • DomEvent.DoubleTap
    • Draggable拖拽实现
    • PosAnimation位置动画介绍
  • control

  • geometry

  • geo

  • layer

  • Map

  • 《Leaflet源码》笔记
  • dom模块
东流
2025-03-20
目录

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

# 源码详解

  1. 变量定义与兼容性处理
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
  • ​ 目的:根据浏览器是否支持 MSPointer(旧版 IE)选择对应的事件名称,实现兼容性适配。 ​- 关键点:Browser.msPointer 是 Leaflet 内部的浏览器特性检测标志,用于判断是否为旧版 IE。 ​
  1. 事件类型映射
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
  • ​ 目的:将标准触摸事件类型(如 touchstart)映射到实际的指针事件(如 pointerdown)和对应的处理函数。

  • 关键点:

    • pEvent:事件名映射表。
    • handle:事件处理函数映射表,统一入口处理不同事件。
  1. 指针状态管理
var _pointers = {}; // 存储当前活动的指针(如多个触控点)
var _pointerDocListener = false; // 标记是否已全局监听文档级指针事件
1
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
  • 逻辑流程:

    1. 当添加 touchstart 监听时,触发 _addPointerDocListener(),全局监听文档级指针事件。
    2. 将用户传入的 handler 绑定到对应的事件处理函数(如 _onPointerStart)。
    3. 使用 addEventListener 注册事件。
  • 关键点:

    • bind(this, handler) 确保处理函数中的 this 指向正确。
    • 返回包装后的 handler,便于后续移除监听。 ​
  1. 全局指针事件监听
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
  • ​ 目的:在文档级别捕获所有指针事件,确保即使指针移动到元素外部,仍能跟踪状态。
  • 关键点:
    • 使用 捕获阶段(true) 监听事件,确保优先处理。
    • _globalPointerDown/_globalPointerMove/_globalPointerUp 维护 _pointers 对象。 ​
  1. 指针状态更新
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
  • ​ 目的:维护 _pointers 对象,记录所有活动指针的实时数据。
  1. 事件处理与转换
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
  • ​ 关键点:
    • 过滤鼠标事件:仅处理触控笔或手指触控。
    • ​ 模拟触摸事件结构:
      • e.touches: 所有当前活动的触控点。
      • e.changedTouches: 当前发生变化的触控点(例如本次事件的触控点)。
  1. 触摸开始事件处理
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
  • 目的:在触摸开始时,阻止浏览器默认行为(如页面滚动),确保 Leaflet 能完全控制交互。
  • 关键点:
    • 仅对触摸类型(非鼠标/触控笔)阻止默认行为。 ​

# 总结

  • 核心逻辑: Leaflet 通过抽象指针事件,将其转换为统一的触摸事件,解决了不同浏览器(尤其是旧版 IE)的兼容性问题,并支持多点触控操作。

  • ​ 设计亮点:

    • 全局指针跟踪:通过文档级监听维护所有指针状态。
    • 事件结构模拟:将 PointerEvent 转换为类似 TouchEvent 的结构,统一上层逻辑。
    • 性能优化:仅在需要时(如首次 touchstart)添加全局监听,避免资源浪费。 ​
  • 适用场景: 地图拖拽、缩放、绘制等需要处理复杂触控交互的功能。

编辑 (opens new window)
上次更新: 2025/03/21, 01:49:37
DomUtil
DomEvent.DoubleTap

← DomUtil DomEvent.DoubleTap→

最近更新
01
GeoJSON
05-08
02
Circle
04-15
03
CircleMarker
04-15
更多文章>
Theme by Vdoing | Copyright © 2024-2025 东流 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式