唯杰地图 logo唯杰地图
VJMAP
EN

zoom-behavior

vjmap/references/marker/zoom-behavior.md 5.1 KB138 行
原文

本文回答: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) 路径)。

九、组合最佳实践

  1. 数量 < 500:只用 minZoom/maxZoom 控制层级,开销可接受。
  2. 数量 500~5000:开 removeWhenNoInMapView: true。
  3. 数量 > 5000:改用 Symbol Layer + cluster。

十、常见坑

  1. setMinZoom 不生效:判定要等下一次 zoomend / moveend 才执行;超出区间是摘除 DOM(不是 display),自己持有的 DOM 引用此时不在文档里。
  2. removeWhenNoInMapView 闪烁:视野 padding 太小;建议 >= 500。
  3. scaleMaxZoom 对任意 element 生效(官方示例即用于自定义 DOM):它把 scale() 拼进根节点的 CSS transform,会覆盖你写在根节点上的 transform——自定义动画 / 旋转放到子节点上。
  4. height 高了图标被遮挡:3D fill-extrusion 图层在 Marker 之上;Marker 是 DOM,fixed 在 canvas 之上,但高度是模拟的。