Leaflet加载万级L.circleMarker时如何替换为自定义形状图标
Leaflet万级点量下自定义标记形状实现方案
L.circleMarker本身是矢量绘制的点要素,天生不支持替换自定义图标,1万点量级下直接换普通L.marker挂自定义图标大概率卡成PPT,几个实测过的可行方案按性能排序如下:
- 最优方案:扩展Canvas渲染器自定义绘制形状,性能和原生circleMarker几乎无差
直接改Leaflet默认Canvas渲染器的点绘制逻辑,给点要素加形状配置项,绘制时用原生Canvas API画方形、三角、菱形等你需要的形状,不需要额外加载图标资源,1万点下缩放、平移帧率稳60帧。核心实现代码:
用的时候和原来写// 扩展Canvas渲染器的自定义形状绘制方法 L.Canvas.include({ _updateShapeMarker: function(layer) { if (!layer._point) return; const ctx = this._ctx, radius = layer._radius, point = layer._point; ctx.beginPath(); // 根据配置的形状类型走不同绘制逻辑 switch(layer.options.shape) { case 'square': ctx.rect(point.x - radius, point.y - radius, radius*2, radius*2); break; case 'triangle': ctx.moveTo(point.x, point.y - radius); ctx.lineTo(point.x + radius, point.y + radius); ctx.lineTo(point.x - radius, point.y + radius); ctx.closePath(); break; case 'diamond': ctx.moveTo(point.x, point.y - radius); ctx.lineTo(point.x + radius, point.y); ctx.lineTo(point.x, point.y + radius); ctx.lineTo(point.x - radius, point.y); ctx.closePath(); break; // 其他形状按需补Canvas绘制逻辑即可 default: // 默认走原来的圆形逻辑 ctx.arc(point.x, point.y, radius, 0, Math.PI*2); } this._fillStroke(ctx, layer); } }) // 自定义标记类继承CircleMarker,复用原有所有交互、样式逻辑 const ShapeMarker = L.CircleMarker.extend({ _updatePath: function() { this._renderer._updateShapeMarker(this); } })L.circleMarker完全一致,多传一个shape参数指定形状就行,原来的填充色、边框、透明度、点击事件这些配置全部兼容,没有额外学习成本。 - 次选方案:Canvas渲染模式的图标Marker
如果你需要用设计好的图片图标,不要用默认DOM渲染的L.marker,选支持Canvas批量绘制图标的Marker扩展,所有点统一在一个Canvas画布上绘制,不会生成上万个DOM节点拖慢页面,1万点下帧率基本能维持在45帧以上,足够日常使用。 - 兜底优化:配合点聚合
如果后续点量涨到3万以上,低缩放级别下全量绘制哪怕是Canvas也会有压力,搭配点聚合逻辑,低缩放级别只展示聚合后的统计点,放大到足够层级再渲染单个带形状的点,能大幅降低性能消耗。
踩坑提醒:别尝试给circleMarker改CSS、套DOM结构这类操作,Canvas模式下circleMarker根本没有独立DOM节点,SVG模式下修改上万个SVG元素的样式性能还不如直接用Canvas自定义绘制。


内容的提问来源于stack exchange,提问作者john williams
相关产品推荐
相关产品推荐

