Map类扩展方法之BoxZoom
# 概述
Map.BoxZoom
是Map
类的一个扩展方法,用于实现地图框选缩放功能,属于Leaflet 内部的事件处理器之一。
# 源码分析
# 源码实现
Map.BoxZoom
源码实现如下:
Map.mergeOPtions({
boxZoom: true, //合并配置,默认启用框选缩放功能。
});
// 继承于`Handler`类,用于处理地图事件。
export var BoxZoom = Handler.extend({
initialize: function (map) {
this._map = map; // 关联地图实例
this._container = map._container; // 地图容器元素
this._pane = map._panes.overlayPane; // 覆盖层面板
this._resetStateTimeout = 0; // 重置状态的计时器
map.on("unload", this._destroy, this); // 地图卸载时触发清理方法
},
addHooks: function () {
DomEvent.on(this._container, "mousedown", this._onMouseDown, this);
},
removeHooks: function () {
DomEvent.off(this._container, "mousedown", this._onMouseDown, this);
},
moved: function () {
return this._moved;
},
_destroy: function () {
DomUtil.remove(this._pane); //地图卸载时清理覆盖层
delete this._pane;
},
_resetState: function () {
this._resetStateTimeout = 0;
this._moved = false;
},
_clearDeferredResetState: function () {
if (this._resetStateTimeout !== 0) {
clearTimeout(this._resetStateTimeout);
this._resetStateTimeout = 0;
}
},
_onMouseDown: function (e) {
if (!e.shiftKey || (e.which !== 1 && e.button !== 1)) { // 判断是否按下了Shift键和鼠标左键
return false;
}
//重置状态
this._clearDeferredResetState();
this._resetState();
DomUtil.disableTextSelection(); // 禁用文本选择
DomUtil.disableImageDrag(); // 禁用图片拖拽
this._startPoint = this._map.mouseEventToContainerPoint(e); // 记录起始位置
// 绑定全局鼠标移动、抬起、按键事件
DomEvent.on(
document,
{
contextmenu: DomEvent.stop,
mousemove: this._onMouseMove,
mouseup: this._onMouseUp,
keydown: this._onKeyDown,
},
this
);
},
_onMouseMove: function (e) {
if (!this._moved) {
this._moved = true;
this._box = DomUtil.create("div", "leaflet-zoom-box", this._container); // 创建框选矩形
DomUtil.addClass(this._container, "leaflet-crosshair"); //添加十字光标样式
this._map.fire("boxzoomstart"); // 触发框选开始事件
}
// 更新框选矩形的位置和大小
this._point = this._map.mouseEventToContainerPoint(e);
var bounds = new Bounds(this._point, this._startPoint),
size = bounds.getSize();
DomUtil.setPosition(this._box, bounds.min);
this._box.style.width = size.x + "px";
this._box.style.height = size.y + "px";
},
_finish: function () {
if (this._moved) {
DomUtil.remove(this._box);
DomUtil.removeClass(this._container, "leaflet-crosshair");
}
DomUtil.enableTextSelection();
DomUtil.enableImageDrag();
DomEvent.off(
document,
{
contextmenu: DomEvent.stop,
mousemove: this._onMouseMove,
mouseup: this._onMouseUp,
keydown: this._onKeyDown,
},
this
);
},
_onMouseUp: function (e) {
if (e.which !== 1 && e.button !== 1) {
return;
}
// 清理DOM和事件
this._finish();
if (!this._moved) {
return;
}
this._clearDeferredResetState();
this._resetStateTimeout = setTimeout(Util.bind(this._resetState, this), 0);
var bounds = new LatLngBounds(
this._map.containerPointToLatLng(this._startPoint),
this._map.containerPointToLatLng(this._point)
);
// 缩放地图,触发boxzoomend事件
this._map.fitBounds(bounds).fire("boxzoomend", { boxZoomBounds: bounds });
},
_onKeyDown: function (e) {
if (e.keyCode === 27) { // ESC键
this._finish();
this._clearDeferredResetState();
this._resetState();
}
},
});
Map.addInitHook("addHandler", "boxZoom", BoxZoom);
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# 源码详细分析
# BoxZoom
类
- 初始化
initialize
initialize
方法用于初始化BoxZoom
类的实例,接受一个map
参数,该参数是Map
类的实例,表示要绑定事件的地图对象。在initialize
方法中,首先将map
对象赋值给this._map
属性,然后获取地图容器元素和覆盖层元素,并将其赋值给this._container
和this._pane
属性。接着,将unload
事件绑定到_destroy
方法上,用于在地图卸载时销毁BoxZoom
类的实例。
- 钩子函数
addHooks
/removeHooks
addHooks
方法用于添加事件钩子,将mousedown
事件绑定到_onMouseDown
方法上。
removeHooks
方法用于移除事件钩子,将mousedown
事件从_onMouseDown
方法上解绑。
- 鼠标状态
moved
方法
moved
方法用于判断是否发生了移动操作,返回一个布尔值,表示是否发生了移动操作。
- 销毁方法
_destroy
_destroy
方法用于销毁BoxZoom
类的实例,移除覆盖层元素和禁用文本选择和图像拖动。
- 重置状态
_resetState
_resetState
方法用于重置状态,将this._resetStateTimeout
属性设置为0
,将this._moved
属性设置为false
。
- 清除延迟重置状态
_clearDeferredResetState
_clearDeferredResetState
方法用于清除延迟重置状态,将this._resetStateTimeout
属性设置为0。
鼠标按下事件
_onMouseDown
_onMouseDown
方法用于处理鼠标按下事件,接受一个e
参数,该参数是鼠标事件对象。 在_onMouseDown
方法中,首先判断是否按下了Shift
键和鼠标左键或右键,如果不满足条件,则返回false
。 然后,调用_clearDeferredResetState
方法清除延迟重置状态,调用_resetState
方法重置状态。 接着,禁用文本选择和图像拖动,将鼠标按下的容器点赋值给this._startPoint
属性。 最后,将contextmenu
事件绑定到DomEvent.stop
方法上,将mousemove
事件绑定到_onMouseMove
方法上,将mouseup
事件绑定到_onMouseUp
方法上,将keydown
事件绑定到_onKeyDown
方法上。鼠标移动事件
_onMouseMove
_onMouseMove
方法用于处理鼠标移动事件,接受一个e
参数,该参数是鼠标事件对象。 在_onMouseMove
方法中,首先判断是否发生了移动操作,如果没有发生,则将this._moved
属性设置为true。 然后,创建一个div
元素,设置其类名为leaflet-zoom-box
,并将其添加到this._container
元素中。 接着,将leaflet-crosshair
类添加到this._container
元素中。 最后,调用this._map.fire("boxzoomstart")
方法触发boxzoomstart
事件。完成处理
_finish
框选结束鼠标抬起时会触发_finish
方法,用于进行完成操作,如移除div
元素、禁用文本选择和图像拖动以及移除全局绑定的监听事件。
- 鼠标抬起事件
_onMouseUp
_onMouseUp
方法用于处理鼠标抬起事件,接受一个e
参数,该参数是鼠标事件对象。
在_onMouseUp
方法中,首先判断是否抬起了鼠标左键或右键,如果不满足条件,则返回。然后,调用_finish
方法完成操作,调用_clearDeferredResetState
方法清除延迟重置状态,调用_resetStateTimeout
方法设置延迟重置状态。最后,将鼠标抬起的容器点赋值给this._point
属性,计算出矩形框的左上角和右下角的经纬度坐标,创建一个LatLngBounds
对象,将其赋值给bounds
变量。然后,调用this._map.fitBounds(bounds).fire("boxzoomend", { boxZoomBounds: bounds })
方法将地图缩放到矩形框的范围内,并触发boxzoomend
事件,将矩形框的边界传递给事件处理函数。
_onKeyDown
键盘按下事件_onKeyDown
方法用于处理键盘按下事件,接受一个e
参数,该参数是键盘事件对象。 在_onKeyDown
方法中,首先判断是否按下了Esc
键,如果按下了,则调用_finish
方法完成操作,调用_clearDeferredResetState
方法清除延迟重置状态,调用_resetState
方法重置状态。
# Map
类
Map.addInitHook
方法是将BoxZoom
注册为Leaflet的默认处理器,使得地图初始化时会自动加载。
而Map.addInitHook
方法的第一个参数addHandler
就是用于添加处理器,它接受三个参数,分别是name
、handler
和context
。而在上述中,name
和handler
对应的就是boxZoom
字符串和BoxZoom
类,首先判断是否存在this._handlers
属性,如果不存在,则创建一个空对象。然后,将handler
对象赋值给this._handlers[name]
属性,即this._handlers.boxZoom= new BoxZoom(this)
。在最开始合并选项时默认boxZoom
为true
,所以会执行this._handlers.boxZoom.enable()
即启用处理器。enable()
方法是在Handler
基类中实现的,enable
方法内部会调用this.addHooks()
方法,即添加钩子函数,监听mousedown
事件。
# 总结
Map.BoxZoom
是通过继承 Handler
类,实现了框选缩放的核心交互逻辑:
- 事件监听:通过钩子函数绑定/解绑鼠标和键盘事件。
- DOM 操作:动态创建/更新框选矩形,并管理相关样式。
- 地理计算:将屏幕坐标转换为地理边界,触发地图缩放。
- 状态管理:处理中途取消(
ESC
键)、资源清理等边界条件。
它是 Leaflet 高可扩展性的典型示例,通过自定义 Handler
可灵活添加或修改交互行为。