zoom-behavior
本文回答:Marker 随缩放级别的可见性、自动缩放、视野裁剪 —— 让 Marker 在合适的 zoom 才出现。
注意:
minZoom/maxZoom/scaleMaxZoom/height/removeWhenNoInMapView是 vjmap 扩展字段,new Marker()构造器不读它们——须用vjmap.createMarker({...})或运行时 setter(setMinZoom/setScaleMaxZoom/setHeight/setRemoveWhenNoInMapView)。
一、minZoom / maxZoom(显示级别区间)
vjmap.createMarker({ minZoom: 10, maxZoom: 18 })
.setLngLat([lng, lat]).addTo(map);
当前 zoom 在 [10, 18) 区间内才显示(zoom >= minZoom && zoom < maxZoom)。
运行时:
marker.setMinZoom(12);
marker.setMaxZoom(20);
原理:超出区间时内部 remove(true) 把 DOM 从文档摘除(触发 autoRemove),回到区间重新插入(触发 autoAdd),不是 display:none。
⚠️ 可见性判定挂在地图
zoomend/moveend事件上——addTo时不做初始评估,setMinZoom/setMaxZoom之后也要等下一次 zoom 结束才生效(源码:_options._minZoom/_maxZoom在 zoomend 回调里比较)。要立刻生效得触发一次缩放(map.zoomTo(..., {duration:0}))。
二、scaleMaxZoom(低 zoom 自动缩小)
vjmap.createMarker({ scaleMaxZoom: 16 })
.setLngLat([lng, lat]).addTo(map);
zoom >= 16:正常大小zoom < 16:按2^(zoom - 16)指数比例缩小(每低一级再减半:15 级→0.5、14 级→0.25),避免低 zoom 下图标互相挤堆。
运行时:
marker.setScaleMaxZoom(14);
三、removeWhenNoInMapView(视野裁剪 / 回收复用)
vjmap.createMarker({
removeWhenNoInMapView: true,
removeWhenNoInMapViewPadding: 400 // 默认 500
})
.setLngLat([lng, lat]).addTo(map);
- 平移地图时:Marker 离开视野(+padding 范围)自动从文档 DOM 移除;回到视野再回插 DOM。
- 事件:
autoAdd/autoRemove。
场景:几千个 Marker 时,只在视野内渲染,性能大幅提升。
坑:如果 Marker 是 Vue/React 组件渲染,每次 remove/add DOM 都会触发 onMount/onUnmount,可能重新触发网络请求。
四、随 pitch 变高度(3D 悬浮)
vjmap.createMarker({ height: 300 }) // 300 米高
.setLngLat([lng, lat]).addTo(map);
设置 height 后,Marker 显示在"离地面 300 米"的位置。map.setPitch(60) 才看得到。
运行时:
marker.setHeight(500);
五、多级显隐(典型写法)
// 一级:仅 >=15 显示
const markerHigh = vjmap.createMarker({ minZoom: 15 })
.setLngLat([lng1, lat1]);
markerHigh.addTo(map);
// 二级:仅 >=12 显示
const markerMid = vjmap.createMarker({ minZoom: 12 })
.setLngLat([lng2, lat2]);
markerMid.addTo(map);
// 三级:全局显示
const markerAll = new vjmap.Marker()
.setLngLat([lng3, lat3]);
markerAll.addTo(map);
六、监听缩放决定行为
map.on("zoomend", () => {
const z = map.getZoom();
if (z > 18) {
marker.getElement().style.background = "#f00";
} else {
marker.getElement().style.background = "";
}
});
七、isHidden / show / hide
marker.isHidden(); // true 表示因缩放级别不在区间 / 视野外 / hide()
marker.show(); // 显示
marker.hide(); // 隐藏
八、setVisible
marker.setVisible(false, true); // isDisplay=true:display:none,等价 hide()
marker.setVisible(true, true); // display:"",等价 show()
marker.setVisible(false, false); // isDisplay 假分支:调 this.remove(),等价 remove()
marker.setVisible(true, false); // 调 addTo(this._map):仅对"自动摘除但 _map 还在"的 marker 有意义
isDisplay=true 走 CSS display 显隐(DOM 保留);isDisplay 假则走 addTo/remove 真挂真摘。
⚠️ setVisible(false,false) 是真移除(_map 已删),之后 setVisible(true,false) 会 addTo(undefined) 报错——恢复要用 marker.addTo(map)。需要"摘除 DOM 但保留登记"请用 removeWhenNoInMapView(内部 remove(true) 路径)。
九、组合最佳实践
- 数量 < 500:只用 minZoom/maxZoom 控制层级,开销可接受。
- 数量 500~5000:开
removeWhenNoInMapView: true。 - 数量 > 5000:改用 Symbol Layer + cluster。
十、常见坑
- setMinZoom 不生效:判定要等下一次
zoomend/moveend才执行;超出区间是摘除 DOM(不是 display),自己持有的 DOM 引用此时不在文档里。 - removeWhenNoInMapView 闪烁:视野 padding 太小;建议 >= 500。
- scaleMaxZoom 对任意 element 生效(官方示例即用于自定义 DOM):它把
scale()拼进根节点的 CSS transform,会覆盖你写在根节点上的 transform——自定义动画 / 旋转放到子节点上。 - height 高了图标被遮挡:3D fill-extrusion 图层在 Marker 之上;Marker 是 DOM,fixed 在 canvas 之上,但高度是模拟的。