图层叠加与顺序
地图上的压盖关系由图层顺序决定,但顺序有两个来源:数组位置和 zIndex。 两者同时存在时,zIndex 说了算,这也是「明明把图层放到最后却还是被盖住」的原因。
图层集合就是一个数组
map.getLayers() 返回 ol.Collection,
getArray() 拿到真实数组。数组前面的图层先绘制,
后面的盖在上面。
var base = new ol.layer.Tile({
source: new ol.source.OSM(),
zIndex: 0
});
var parcels = new ol.layer.Vector({
source: new ol.source.Vector({ url: '/data/parcels.geojson', format: new ol.format.GeoJSON() }),
zIndex: 10,
opacity: 0.85
});
var labels = new ol.layer.Vector({
source: labelSource,
zIndex: 20,
declutter: true
});
var map = new ol.Map({
target: 'map',
layers: [base, parcels, labels],
view: new ol.View({
center: ol.proj.fromLonLat([116.397, 39.908]),
zoom: 12
})
});
opacity 作用于整层,包含标注。需要「面半透明、点不透明」时,
把面和点拆成两个图层,各自设透明度。
| 控制项 | API | 作用范围 |
|---|---|---|
| 数组顺序 | getLayers().insertAt / removeAt | 整体重排,代价是一次重绘 |
| zIndex | layer.setZIndex(n) | 数值大的在上,未设置视为 0 |
| 可见性 | layer.setVisible(false) | 整层隐藏,不参与渲染 |
| 透明度 | layer.setOpacity(0.5) | 整层混合,含所有子要素 |
| 范围 | layer.setExtent(rect) | 只渲染指定范围,可裁掉无关瓦片 |
zIndex 与数组顺序的优先级
OpenLayers 在每次渲染前按 zIndex 排序图层。只要给图层设了不同的
zIndex,数组里的位置就不影响最终结果。
全部为默认值 0 时,才按数组顺序绘制。
// 这两个写法结果不同
map.getLayers().insertAt(0, highlightLayer); // 数组最前面
highlightLayer.setZIndex(5); // 但 zIndex 更大,画在最上面
// 想让高亮真正压在最上层,zIndex 要大于其它图层
map.getLayers().forEach(function (layer) {
if (layer !== highlightLayer) {
layer.setZIndex(layer.getZIndex() + 1);
}
});
highlightLayer.setZIndex(100);
动态调整时优先改 zIndex,不要频繁重排数组。
重排会让渲染器重新计算图层组,图层多时有可感知的卡顿。
用组图层成批管理
ol.layer.Group 把多个图层打包,对组设置可见性和透明度,
效果会传递到组内所有图层。专题切换、底图分组都适合用它。
var business = new ol.layer.Group({
layers: [parcels, labels],
zIndex: 10
});
map.addLayer(business);
// 整组隐藏,组内图层自身的 visible 状态保持不变
business.setVisible(false);
// 组内再叠加一个图层,仍然受组控制
business.getLayers().push(waterLayer);
// 组透明度会与子图层透明度相乘
business.setOpacity(0.6);
组透明度会相乘。组设 0.6、子图层设 0.5,最终是 0.3。 排查「太淡了」的问题时,先看组和子图层是不是都设过透明度。
底图切换的常见结构
多个底图不要全部添加再切换可见性,未显示的瓦片图层不会请求数据, 但保留在图层集合里仍会增加渲染调度。更干净的做法是只保留当前底图。
var osm = new ol.layer.Tile({ source: new ol.source.OSM(), zIndex: 0 });
var imagery = new ol.layer.Tile({
source: new ol.source.XYZ({
url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
attributions: '© OpenStreetMap contributors'
}),
zIndex: 0
});
function switchBase(next) {
map.getLayers().getArray().forEach(function (layer) {
if (layer.get('isBase')) map.removeLayer(layer);
});
next.set('isBase', true);
map.getLayers().insertAt(0, next);
}
switchBase(osm);
注意事项
zIndex只在同一地图内比较,嵌套组图层时组内图层仍按自身 zIndex 排序。- 矢量图层与瓦片图层混合时,矢量总是绘制在瓦片之上,除非瓦片被放进更高的组。
- 频繁
setZIndex会触发重绘,批量调整后再统一渲染一次更划算。 - 图层
extent只是裁剪显示范围,不会减少数据源请求。 - 地图销毁时要调用
map.setTarget(null),否则图层与事件监听会残留。
经纬志(2026)。《图层叠加与顺序》。https://www.jingweizhi.com/openlayers/tutorial/layer-order.html(访问日期 2026-11-03)。