Skip to content

弹窗与提示框

本文档涵盖 Leaflet 中的弹窗和提示框覆盖层系统。弹窗和提示框是基于 HTML 的覆盖层,在特定地理位置的地图图层上方显示信息。两者都扩展自共同的 DivOverlay 基类,可以绑定到图层或独立显示。

关于其他覆盖层类型(如图片覆盖层)的信息,请参阅 瓦片图层与网格系统。关于也可以绑定弹窗/提示框的图层组和要素组的信息,请参阅 图层组与要素组

架构概览

覆盖层系统建立在三层类层次结构上,其中 DivOverlayPopupTooltip 提供基础。

类层次结构

SVG
100%

DivOverlay 基类

DivOverlay 类为所有位于地理坐标位置的基于 HTML 的覆盖层提供共享功能。

特性描述实现
内容管理支持字符串、HTMLElement 或函数内容src/layer/DivOverlay.js158-171
位置绑定将覆盖层链接到 LatLng 坐标src/layer/DivOverlay.js141-156
源跟踪引用打开覆盖层的图层src/layer/DivOverlay.js45-56
交互模式覆盖层容器上的可选事件监听src/layer/DivOverlay.js120-123
Pane 管理通过地图 pane 系统控制 z-indexsrc/layer/DivOverlay.js34-36
FeatureGroup 支持自动从组中选择可见图层src/layer/DivOverlay.js232-269

关键方法:

Popup 类实现模态风格的覆盖层,具有自动关闭行为和视口管理。

配置选项

SVG
100%

DOM 结构

弹窗创建分层的 HTML 结构,包含内容包装器、提示(箭头)和可选的关闭按钮。

SVG
100%

自动平移机制

autoPan: true 时,地图自动平移以确保弹窗在视口中完全可见。

SVG
100%

计算逻辑:

  1. 将弹窗位置转换为容器点 src/layer/Popup.js298
  2. 计算每个方向的溢出(右、左、下、上)src/layer/Popup.js306-317
  3. 应用 padding 偏移 src/layer/Popup.js299-302
  4. 如果非零则按计算的 delta 平移 src/layer/Popup.js323-332

自动关闭行为

弹窗实现类似单例的行为,打开新弹窗会自动关闭前一个。

场景行为实现
使用 autoClose: true 打开新弹窗关闭 map._popup(如果存在)src/layer/Popup.js138-147
使用 autoClose: false 打开弹窗多个弹窗可以共存src/layer/Popup.js141
使用 closePopupOnClick: true 点击地图通过 preclick 事件关闭弹窗src/layer/Popup.js193-205
使用 closeOnEscapeKey: true 按下 ESC 键关闭弹窗(由地图处理)src/layer/Popup.js114-117

使用 ResizeObserver 的内容跟踪

弹窗使用 ResizeObserver 检测内容大小变化(例如图片加载)并自动重新定位。

SVG
100%

Tooltip 类

Tooltip 类实现轻量级、非模态覆盖层,具有方向定位和指针跟随功能。

配置选项

选项默认值描述
direction'auto'放置位置:'right', 'left', 'top', 'bottom', 'center', 'auto'
permanentfalse永久显示而不是悬停时显示
stickyfalse跟随指针而不是固定位置
opacity0.9容器不透明度
offset[0, 0]像素位置偏移
pane'tooltipPane'用于渲染的地图 pane

方向定位

提示框根据方向计算位置,当方向为 'auto' 时自动切换到 'left''right'

SVG
100%

定位算法:

  1. 确定方向(自动或显式)src/layer/Tooltip.js165-188
  2. 从提示框尺寸计算偏移 src/layer/Tooltip.js165-188
  3. 应用 CSS 类进行方向样式设置 src/layer/Tooltip.js192-199
  4. 设置最终 DOM 位置 src/layer/Tooltip.js199

Sticky 模式

sticky: true 时,提示框跟随指针而不是锚定到固定位置。

SVG
100%

永久与临时提示框

提示框可以是永久的(始终可见)或临时的(悬停/聚焦时显示)。

模式触发条件事件监听器
永久 (permanent: true)图层添加时立即显示add, remove, move 事件
临时(默认)用户交互pointerover, pointerout, click, focus, blur, move, remove

临时提示框事件绑定:

pointerover -> _openTooltip()
pointerout -> closeTooltip()
click -> _openTooltip()
focus -> _openTooltip() (通过 _addFocusListeners)
blur -> closeTooltip()

辅助功能

提示框实现 ARIA 属性以支持屏幕阅读器辅助功能:

  1. Role 属性:容器具有 role="tooltip" src/layer/Tooltip.js146
  2. 唯一 ID:每个提示框获得 id="leaflet-tooltip-{stamp}" src/layer/Tooltip.js147
  3. aria-describedby:绑定的图层元素引用提示框 ID src/layer/Tooltip.js414-417

图层绑定 API

弹窗和提示框都通过绑定方法扩展 Layer 类,将覆盖层附加到图层并管理交互事件。

绑定方法

SVG
100%

事件处理程序注册

当调用 bindPopup()bindTooltip() 时,图层注册事件处理程序来管理覆盖层可见性。

弹窗事件处理程序:

click -> _openPopup()
keypress -> _onKeyPress() (在 Enter 键上打开)
remove -> closePopup()
move -> _movePopup()

提示框事件处理程序(非永久):

pointerover -> _openTooltip()
pointerout -> closeTooltip()
click -> _openTooltip()
focus -> _openTooltip() (通过 _addFocusListeners)
blur -> closeTooltip()
pointermove -> _moveTooltip() (如果 sticky: true)

基于函数的内容

两个覆盖层都支持接收源图层作为参数的函数内容,实现基于图层属性的动态内容。

// 来自测试套件的示例
marker.description = 'I am a marker.';
marker.bindPopup(layer => layer.description);

// 带多个图层的 FeatureGroup
const group = new FeatureGroup([marker1, marker2]);
group.bindTooltip(layer => `Tooltip: ${layer.options.description}`);

实现:

定位和锚定

覆盖层相对于其地理坐标定位,具有来自源图层的可选偏移和锚点。

位置计算流程

SVG
100%

锚点

图层提供锚点,指定覆盖层应相对于图层视觉表示定位的位置。

图层类型锚点方法用途
Marker_getPopupAnchor()在图标的弹窗锚点位置定位
Marker_getTooltipAnchor()在图标的提示框锚点位置定位
Path(无锚点方法)使用默认 [0, 0] 锚点

默认行为:

偏移系统

两个覆盖层都支持像素偏移以微调定位:

事件系统

弹窗和提示框在打开或关闭时在地图和源图层上触发事件。

事件流程图

SVG
100%

事件类型

事件触发于载荷何时触发
popupopenMap, Layer{popup: Popup}弹窗添加到地图
popupcloseMap, Layer{popup: Popup}弹窗从地图移除
autopanstartMap{...}自动平移开始
tooltipopenMap, Layer{tooltip: Tooltip}提示框添加到地图
tooltipcloseMap, Layer{tooltip: Tooltip}提示框从地图移除
contentupdateDivOverlay{...}内容更新

事件传播

图层事件通过事件父级向上传播:

  1. 覆盖层在自身上触发事件
  2. 事件在源图层上以 propagate: true 触发 src/layer/Popup.js163 src/layer/Tooltip.js106
  3. 事件冒泡到图层的事件父级(如果设置)
  4. 地图也直接接收事件 src/layer/Popup.js156 src/layer/Tooltip.js97

地图 API 方法

LeafletMap 类提供处理弹窗和提示框的便捷方法。

地图扩展

SVG
100%

实现:

使用模式

独立覆盖层

独立于图层创建和定位覆盖层:

// 带位置和选项的弹窗
const popup = new Popup(latlng, {content: 'Hello world!'})
    .openOn(map);

// 带流畅 API 的提示框
const tooltip = new Tooltip()
    .setLatLng(latlng)
    .setContent('Tooltip text')
    .addTo(map);

绑定到标记

最常见的模式将覆盖层绑定到具有自动定位的标记:

// 绑定弹窗到标记
marker.bindPopup('Popup content').openPopup();

// 带选项绑定提示框
marker.bindTooltip('Tooltip text', {
    direction: 'top',
    permanent: true
});

切换行为:点击绑定弹窗的标记会切换其打开/关闭状态 src/layer/Popup.js478-498

绑定到矢量图层

路径(折线、多边形)支持在点击点或路径中心定位的弹窗/提示框:

const polygon = new Polygon(coordinates);
polygon.bindPopup('Polygon info');
polygon.bindTooltip('Hover tooltip');

矢量图层不会在点击时切换弹窗 - 它们打开弹窗并阻止地图点击传播 src/layer/Popup.js282-299

绑定到 FeatureGroups

FeatureGroups 在多个图层之间共享单个弹窗/提示框,内容基于交互的图层动态变化:

const group = new FeatureGroup([marker1, marker2]);
group.bindTooltip(layer => `Info for ${layer.options.name}`);

当组中的图层被点击/悬停时,覆盖层的 _source 被设置为该特定图层 src/layer/DivOverlay.js236-248

防止地图拖拽干扰

提示框在打开前检查地图是否正在拖拽,以避免指针移动快于地图时闪烁:

// _openTooltip 中的内部逻辑
if (this._map.dragging?.moving()) {
    // 如果图层刚添加则延迟打开到 moveend
    if (e.type === 'add' && !this._moveEndOpensTooltip) {
        this._moveEndOpensTooltip = true;
        this._map.once('moveend', () => {
            this._moveEndOpensTooltip = false;
            this._openTooltip(e);
        });
    }
    return;
}

动态内容更新

内容可以在绑定后使用 setContent() 或通过重新绑定来更新:

// 在现有弹窗上更新内容
layer.setPopupContent('New content');

// 通过弹窗实例更新
const popup = layer.getPopup();
popup.setContent('Updated');

// 强制重新渲染
popup.update();

update() 方法重新计算内容、布局和位置 src/layer/DivOverlay.js181-193