唯杰地图 logo唯杰地图
VJMAP
EN

performance-tips

vjmap/references/marker/performance-tips.md 3.8 KB150 行
原文

本文回答:Marker 性能优化 —— 数量阈值、viewport 裁剪、降级到 Symbol 图层。

一、Marker 数量对性能的影响

数量 状态 建议
< 200 流畅 任意使用
200–500 略卡 removeWhenNoInMapView: true
500–2000 明显卡 cluster 或 Symbol layer
2000+ 严重卡顿 必须用 Symbol layer

数据来源:一个 DOM Marker 是一个 div + CSS;每次 zoom/pan 都会重新定位(transform),几千个就 GC 频繁。

二、视野裁剪

vjmap.createMarker({ removeWhenNoInMapView: true, removeWhenNoInMapViewPadding: 500 })
  .setLngLat([lng, lat]).addTo(map);
// removeWhenNoInMapView 是 vjmap 扩展字段:createMarker 内部调 setRemoveWhenNoInMapView;
// 写进 new Marker() 构造参数不生效
  • 不在视野的 Marker 从 DOM 移除(真的移除,不是 display:none)
  • 进入视野再重新插入 DOM
  • 对 2000+ Marker 可以流畅到 500 以下

三、缩小自动缩放

vjmap.createMarker({ scaleMaxZoom: 16 })  // 扩展字段,须走 createMarker

低 zoom 时 div 按比例缩小,减少像素绘制压力。

四、复用 DOM

一个 Marker 被 remove 后,下次 new 时 DOM 会重建。大数量场景推荐对象池:

const pool = [];
function getMarker() {
  return pool.pop() || new vjmap.Marker();
}
function releaseMarker(m) {
  m.remove();
  pool.push(m);
}

五、不要在循环里做 map.project

// 慢
for (const m of markers) {
  const p = map.project(m.getLngLat());
  ...
}

// 快:只在 zoomend / moveend 做
map.on("moveend", () => {
  for (const m of markers) {
    const p = map.project(m.getLngLat());
    ...
  }
});

六、降级到 Symbol 图层

DOM Marker 无法承受几万点;改用 vjmap Symbol 图层(WebGL):

map.addSource("pts", {
  type: "geojson",
  data: { type: "FeatureCollection", features: [...] }
});

await map.loadImageEx("pin", "/icons/pin.png");

map.addLayer({
  id: "pts-symbol",
  type: "symbol",
  source: "pts",
  layout: {
    "icon-image": "pin",
    "icon-size": 0.6,
    "icon-allow-overlap": true,
    "text-field": ["get", "name"],
    "text-size": 10,
    "text-offset": [0, 1.2],
    "text-anchor": "top"
  },
  paint: { "text-color": "#000", "text-halo-color": "#fff", "text-halo-width": 1 }
});

优点:几十万点依然 60 FPS。

七、Marker vs Symbol 选型

特性 Marker (DOM) Symbol (WebGL)
数量 < 500 任意
自定义 100% 随意 CSS/HTML 受限(icon + text)
动画 CSS/JS 自由 只能 paint 插值
交互 click/hover 原生 map.on("click", layerId, ...)
旋转倾斜 需设置 alignment 原生支持
成本 高 极低

八、热力 / 聚合替代 Marker

几万点显示"热度",用 heatmap 图层:

map.addLayer({
  id: "heat",
  type: "heatmap",
  source: "pts",
  paint: {
    "heatmap-weight": 1,
    "heatmap-intensity": 1,
    "heatmap-radius": 30
  }
});

九、事件节流

map.on("mousemove", throttle(e => {
  // 高频事件
}, 100));

function throttle(fn, ms) {
  let last = 0;
  return (...args) => {
    const now = Date.now();
    if (now - last > ms) { fn(...args); last = now; }
  };
}

十、常见坑

  1. 一次性 new vjmap.Marker 5000 个 → 页面直接卡死。改为分批或用 Symbol。
  2. 没 remove 旧 Marker → 每次刷新累积,几次后就爆。
  3. 每次 move 都 queryRenderedFeatures → 100 ms 节流。
  4. 用 DOM Marker 做轨迹动画 → 卡顿;轨迹只用一个 Marker + setLngLat。
  5. 没用 will-change: transform → 动画帧偶现闪烁。