Performance with large point sets
Quick answer
This tutorial explains why thousands of Leaflet markers or SVG paths slow down and compares the capacity of L.marker, circleMarker on SVG, circleMarker on canvas, clustering and vector tiles. It shows preferCanvas and L.canvas with a padding option shared by one layer group, then adds viewport culling and chunked insertion on moveend. It closes with smoothFactor, updateWhenZooming and updateWhenIdle for smoother interaction.
Key points
- Enable preferCanvas or pass one shared L.canvas renderer instead of one per layer.
- Insert visible points in chunks inside requestAnimationFrame to avoid blocking the main thread.
- Set updateWhenZooming: false and updateWhenIdle: true when the data set is large.
Example
var map = L.map('map', {
center: [39.907359, 116.391263],
zoom: 10,
preferCanvas: true,
renderer: L.canvas({ padding: 0.5 })
});
var canvasLayer = L.canvas({ padding: 0.5 });
var points = [];
for (var i = 0; i < 50000; i++) {
points.push([
39.5 + Math.random() * 0.9,
116.0 + Math.random() * 0.8
]);
}
var dots = L.layerGroup();
points.forEach(function (ll) {
dots.addLayer(L.circleMarker(ll, {
radius: 3,
stroke: false,
fillColor: '#17694a',
fillOpacity: 0.6,
renderer: canvasLayer // 统一走同一个画布
}));
});
dots.addTo(map);function inView(ll, bounds, pad) {
return ll[0] > bounds.getSouth() - pad &&
ll[0] < bounds.getNorth() + pad &&
ll[1] > bounds.getWest() - pad &&
ll[1] < bounds.getEast() + pad;
}
function renderVisible(all, layer) {
var b = map.getBounds();
var pad = (b.getNorth() - b.getSouth()) * 0.2;
layer.clearLayers();
var batch = [];
for (var i = 0; i < all.length; i++) {
if (inView(all[i], b, pad)) batch.push(all[i]);
}
var step = 800;
(function addChunk(start) {
if (start >= batch.length) return;
for (var j = start; j < Math.min(start + step, batch.length); j++) {
layer.addLayer(L.circleMarker(batch[j], {
radius: 3, stroke: false, fillOpacity: 0.6
}));
}
requestAnimationFrame(function () { addChunk(start + step); });
})(0);
}
map.on('moveend', function () {
renderVisible(allPoints, dots);
});
Notes
- Canvas layers have no CSS hover, so highlight via mouseover and setStyle with a full redraw.
- Every marker carries a shadow image by default; shadowUrl: null halves nodes and requests.
English edition written for Atlaslog. Code is taken from the original Chinese page and runs unchanged.
Atlaslog (2026). Performance with large point sets. https://www.jingweizhi.com/en/leaflet/tutorials/performance-points.html (accessed 2026-11-03).