唯杰地图 logo唯杰地图
VJMAP
EN

marker-vs-symbol

vjmap/references/symbol/marker-vs-symbol.md 3.6 KB130 行
原文

本文回答:Marker (DOM) vs Symbol (WebGL) 选型 —— 数量阈值、自定义能力、性能代价。

一、对比

维度 Marker (DOM) Symbol (WebGL)
渲染 HTML/CSS WebGL 贴图
数量上限 < 500 流畅,>2000 严重卡 10 万+ 流畅
自定义 100% 随意(div/Vue/React) 受限(icon + text)
动画 CSS/JS 自由 paint 属性插值(有限)
交互 addEventListener map.on("click", layerId, ...)
旋转倾斜 需 alignment 原生支持
碰撞避让 手写 原生
图标换色 CSS SDF + iconColor
Vue/React 组件 ✓ ✗
Popup setPopup hoverPopup / clickPopup
加载性能 DOM 构建慢 一次 GPU 上传

二、阈值经验

  • 100 以下:Marker OK
  • 100 – 500:可以 Marker,但建议加 removeWhenNoInMapView
  • 500 – 2000:cluster + Marker,或 circle 层
  • 2000 – 50000:Symbol
  • 50000+:Symbol + cluster + 按 zoom 分层

三、决策流

点数 < 100 且要 Vue/React?  → Marker
点数 >= 100 且要自定义卡片? → Marker + removeWhenNoInMapView
点数 > 500?                 → Symbol
要聚合?                     → Symbol + cluster
要热力?                     → heatmap 层
要沿路径显示?               → Symbol + symbol-placement: line

四、混合:Marker 当选中态 + Symbol 当普通态

// 全量 Symbol
const symbol = new vjmap.Symbol({ data, iconImage: "pin" });
symbol.addTo(map);

let selectedMarker = null;

map.on("click", symbol.getLayerId(), e => {
  const f = e.features[0];
  if (selectedMarker) selectedMarker.remove();
  selectedMarker = new vjmap.Marker({ color: "#f00" })
    .setLngLat(f.geometry.coordinates).addTo(map);

  // 可选:弹出自定义 Popup
  new vjmap.Popup()
    .setLngLat(f.geometry.coordinates)
    .setHTML(`<b>${f.properties.name}</b>`)
    .addTo(map);
});

五、可交互性对比

5.1 hover

Marker:

marker.getElement().addEventListener("mouseenter", () => {});

Symbol:

map.on("mouseenter", symbol.getLayerId(), () => {
  map.getCanvas().style.cursor = "pointer";
});

或 symbol.hoverPointer() / symbol.hoverFeatureState()。

5.2 click

Marker:

marker.getElement().addEventListener("click", ...);

Symbol:

symbol.clickLayer(e => { ... });
// 或
map.on("click", symbol.getLayerId(), e => { ... });

六、迁移思路

从 Marker 迁到 Symbol:

  1. 收集所有 Marker 的 lngLat + properties 做成 GeoJSON:
    const features = markers.map(m => ({
      type: "Feature",
      geometry: { type: "Point", coordinates: [m.getLngLat().lng, m.getLngLat().lat] },
      properties: { ...m.data }
    }));
    
  2. 准备图标(loadImage)
  3. new Symbol + clickLayer
  4. remove 原有 Marker

七、各场景推荐

场景 推荐
单个/几个站点 Marker
国产大屏 KPI 热力 heatmap
几千个设备点位 Symbol(可 cluster)
轨迹动画 一个 Marker + setLngLat
多选列表 + 地图联动 Symbol + 程序化 selection
表单卡片气泡 Marker + Popup 或 Symbol + Popup

八、常见坑

  1. 误用 Marker 画 5000 点 → 严重卡顿,改 Symbol。
  2. 误用 Symbol 放 Vue 组件 → 不支持 DOM;用 Marker 或把 Vue 渲染到 Popup。
  3. Symbol hover 没改色 → feature 没 id,feature-state 不生效。
  4. 用 Marker 做热力 → 代码复杂度高;直接 heatmap 层。