01 · NOTES / WebGIS 开发
第 07 节 · 图层组织与控件
2026 年 9 月 1 日
第 07 节 · 图层组织与控件
📌 版本信息:Leaflet 1.9.4(2026-08-29 核对) 📚 来源:LayerGroup / FeatureGroup | Layers 控件 | Pane 🎯 图层一多(底图 + 业务 N 层),秩序就是架构问题:分组、开关、层级、控件四件事。
一、这一节的目标
- 掌握 LayerGroup / FeatureGroup 的区别与用法
- 精通 Layers 控件:overlay 分类开关的标准 UI
- 理解 pane(渲染层)与 z-index 体系——"谁压住谁"的官方方案
- 掌握自定义控件的写法
二、LayerGroup / FeatureGroup
// LayerGroup:图层的"文件夹"——批量显隐/清空/遍历
const poiGroup = L.layerGroup([m1, m2, m3]).addTo(map);
poiGroup.addLayer(m4); poiGroup.removeLayer(m4); poiGroup.clearLayers();
poiGroup.eachLayer((layer) => console.log(layer.getLatLng()));
// FeatureGroup:LayerGroup + 整体事件与样式能力(_setStyle 全员生效、可整体绑定弹窗)
const cityGroup = L.featureGroup([...markers]).addTo(map);
cityGroup.on('click', (e) => console.log(e.layer)); // 组上挂监听(接近 DOM 的委托体验)
cityGroup.setStyle({ color: 'red' }); // 一键全员改样式
// fitBounds 的最佳拍档:
map.fitBounds(cityGroup.getBounds()); // 自动缩放到"正好装下所有要素"
选型:纯分组显隐用 LayerGroup;要"组级事件/样式/整体 fitBounds"用 FeatureGroup。业务图层永远各占一个 Group(地震层/省界层/标注层)——开关、清空、更新都有落点。
三、Layers 控件:标准开关 UI
// 分两组:底图(单选)与叠加层(多选勾选)
const baseMaps = { 'OSM': osmLayer, '卫星': esriLayer };
const overlays = { '省界': provinceLayer, '地震': quakeLayer, '标注': markers };
L.control.layers(baseMaps, overlays, {
position: 'topright',
collapsed: false, // 默认收起成小图标;false=常开(大屏常用)
autoZIndex: true,
}).addTo(map);
// 勾选框自动调 addLayer/removeLayer——控件就是"图层开关的 UI 化"
// 编程开关:map.addLayer(layer) / map.removeLayer(layer) 也能驱动控件状态(双向同步)
四、Pane:层级秩序的官方方案
问题:自定义 SVG 标注想压在瓦片上、但被弹窗压住;告警标记要压过一切要素……zIndexOffset 只管 marker 之间。Pane = Leaflet 的图层堆叠分区:
// Leaflet 内置 pane(zIndex 从低到高):tilePane 200 → overlayPane 400 → shadowPane 500
// → markerPane 600 → tooltipPane 650 → popupPane 700
// 自定义 pane:先创建(一次性),图层创建时指定 pane
map.createPane('quakes');
map.getPane('quakes').style.zIndex = 450; // 介于瓦片(200)与 overlay(400)…自己定数值
const quakeLayer = L.geoJSON(data, { pane: 'quakes', ... });
//quakeLayer 的所有要素渲染进该 pane——zIndexOffset 不用挨个设了
经验值:业务点层放 markerPane(600) 或自建 450~550;高亮闪烁层更高;确需压过弹窗才 >700(慎用)。
五、自定义控件
// Control.extend:写一个"回到中心"小控件
const HomeControl = L.Control.extend({
options: { position: 'topleft' }, // topleft/topright/bottomleft/bottomright
onAdd(map) { // 控件被加到地图时:返回 DOM
const div = L.DomUtil.create('div', 'home-btn');
div.innerHTML = '⌂';
div.style.cssText = 'width:34px;height:34px;background:#fff;border-radius:6px;'
+ 'display:grid;place-items:center;cursor:pointer;box-shadow:0 1px 4px rgba(0,0,0,.3);font-size:18px';
L.DomEvent.on(div, 'click', (e) => {
L.DomEvent.stopPropagation(e); // 防止点击穿透到地图
map.flyTo([30.5929, 114.3052], 10);
});
return div;
},
});
new HomeControl().addTo(map);
Scale(比例尺)、Attribution(版权)等内置控件同样可配置位置与内容——大屏项目里"左下角比例尺 + 右下角数据时间戳"就是两个自定义控件的组合。
六、动手跟练:07 · 图层开关面板
配套文件:05-leaflet/examples/07-图层开关面板.html
步骤:
- 内容:底图两组(OSM/卫星)+ 三个业务图层(省界面/城市点/自定义 pane 的告警点)+ Layers 控件 + 自定义"回到中心"与"时间戳"控件
- 完成 6 个 TODO:FeatureGroup fitBounds 按钮、pane 创建与告警层压过普通层验证、Layers 控件 collapsed 切换、程序开关图层(按钮调 addLayer/removeLayer 观察控件勾选联动)、自定义控件加第二个(图例框)、Scale 比例尺控件
- 体会"架构感":每个业务图层一个 Group 变量 + Layers 控件注册——这就是 C-06 工作台"图层树"的雏形
通关标准:
- LayerGroup/FeatureGroup 各用对场景
- Layers 控件 + 程序开关双向联动
- 自定义 pane 生效(告警层压过普通层)+ 两个自定义控件
七、自测题
- LayerGroup 与 FeatureGroup 的能力差异?
- fitBounds 的最佳拍档是哪个方法?
- Layers 控件的 baseMaps 与 overlays 的交互差异?
- pane 的 zIndex 体系?业务点层建议放哪?
- 自定义控件的 onAdd 返回什么?点击穿透怎么防?
参考答案
- FeatureGroup 在分组之上加"组级事件与 setStyle"(内部基于 LayerGroup)。
group.getBounds()+map.fitBounds()。- baseMaps 单选(底图互斥),overlays 勾选(叠加可共存)。
- tile 200 / overlay 400 / shadow 500 / marker 600 / tooltip 650 / popup 700;业务点层自建 450~550 或用 markerPane。
- 一个 DOM 元素;L.DomEvent.stopPropagation 阻断穿透。
八、下一步
秩序建立 → 第 08 节:常用插件,站在社区肩膀上装高级能力。