marker-vs-symbol
本文回答: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:
- 收集所有 Marker 的 lngLat + properties 做成 GeoJSON:
const features = markers.map(m => ({ type: "Feature", geometry: { type: "Point", coordinates: [m.getLngLat().lng, m.getLngLat().lat] }, properties: { ...m.data } })); - 准备图标(loadImage)
- new Symbol + clickLayer
- remove 原有 Marker
七、各场景推荐
| 场景 | 推荐 |
|---|---|
| 单个/几个站点 | Marker |
| 国产大屏 KPI 热力 | heatmap |
| 几千个设备点位 | Symbol(可 cluster) |
| 轨迹动画 | 一个 Marker + setLngLat |
| 多选列表 + 地图联动 | Symbol + 程序化 selection |
| 表单卡片气泡 | Marker + Popup 或 Symbol + Popup |
八、常见坑
- 误用 Marker 画 5000 点 → 严重卡顿,改 Symbol。
- 误用 Symbol 放 Vue 组件 → 不支持 DOM;用 Marker 或把 Vue 渲染到 Popup。
- Symbol hover 没改色 → feature 没 id,feature-state 不生效。
- 用 Marker 做热力 → 代码复杂度高;直接 heatmap 层。