Marker Cluster
Cluster 700 points, split them on zoom, and keep click handlers working.
single HTML file · CDN dependencies onlyinterface may contain Chinese labels
Source
<!--
示例:聚合标记
要点:leaflet.markercluster、700 个点分簇、关闭聚合时回退为普通图层组、缩放联动
依赖:Leaflet 1.9.4 · leaflet.markercluster 1.5.3 · jsDelivr CDN + Esri 瓦片
-->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta name="robots" content="noindex,follow">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>聚合标记</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet.markercluster@1.5.3/dist/MarkerCluster.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet.markercluster@1.5.3/dist/MarkerCluster.Default.css">
<style>
html,body{height:100%;margin:0}
body{display:flex;flex-direction:column;overflow:hidden;
font:13px/1.6 -apple-system,"Segoe UI","PingFang SC",sans-serif}
.bar{flex:0 0 auto;padding:8px 12px;background:#f6f7f9;border-bottom:1px solid #e3e6ea;
display:flex;gap:12px;align-items:center;font-size:12px;color:#58606e}
.bar label{display:flex;gap:6px;align-items:center;cursor:pointer}
.bar .spacer{flex:1 1 auto}
#map{flex:1 1 auto;min-height:0}
.dot{width:9px;height:9px;border-radius:50%;background:#1f5fd0;
border:1.5px solid #fff;box-shadow:0 0 0 1px rgba(0,0,0,.18)}
</style>
</head>
<body>
<div class="bar">
<label><input id="cluster" type="checkbox" checked> 启用聚合</label>
<span class="spacer"></span>
<span id="status"></span>
</div>
<div id="map"></div>
<script src="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://cdn.jsdelivr.net/npm/leaflet.markercluster@1.5.3/dist/leaflet.markercluster.js"></script>
<script>
(function () {
'use strict';
function seeded(seed) {
var a = seed;
return function () {
a |= 0;
a = a + 0x6D2B79F5 | 0;
var t = Math.imul(a ^ a >>> 15, 1 | a);
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
return ((t ^ t >>> 14) >>> 0) / 4294967296;
};
}
var rand = seeded(7);
/* 五个聚集中心,模拟门店、设备、采样点一类的分布 */
var centers = [
[39.907, 116.391], [39.945, 116.470], [39.875, 116.342],
[39.960, 116.330], [39.920, 116.430]
];
var data = [];
for (var i = 0; i < 700; i++) {
var c = centers[i % centers.length];
var dLat = (rand() + rand() - 1) * 0.030;
var dLng = (rand() + rand() - 1) * 0.045;
data.push({
lat: c[0] + dLat,
lng: c[1] + dLng,
id: i + 1
});
}
function dotIcon() {
return L.divIcon({
className: '',
html: '<div class="dot"></div>',
iconSize: [9, 9],
iconAnchor: [4.5, 4.5]
});
}
function makeMarker(p) {
return L.marker([p.lat, p.lng], { icon: dotIcon() })
.bindPopup('点位 #' + p.id + '<br>' + p.lat.toFixed(5) + ', ' + p.lng.toFixed(5));
}
var map = L.map('map', {
center: [39.917, 116.391],
zoom: 11
});
L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', {
maxZoom: 19,
attribution: 'Tiles © Esri'
}).addTo(map);
/* 聚合组:chunkedLoading 让大量点分片加入,避免主线程长时间阻塞 */
var clusterGroup = L.markerClusterGroup({
chunkedLoading: true,
maxClusterRadius: 52,
disableClusteringAtZoom: 16,
showCoverageOnHover: false,
spiderfyOnMaxZoom: true
});
var plainGroup = L.layerGroup();
data.forEach(function (p) {
clusterGroup.addLayer(makeMarker(p));
});
data.forEach(function (p) {
plainGroup.addLayer(makeMarker(p));
});
var clustered = true;
clusterGroup.addTo(map);
var status = document.getElementById('status');
var toggle = document.getElementById('cluster');
function refresh() {
status.textContent = data.length + ' 个点 · ' +
(clustered ? '聚合显示' : '逐点显示') + ' · 缩放 ' + map.getZoom();
}
toggle.addEventListener('change', function () {
clustered = toggle.checked;
if (clustered) {
map.removeLayer(plainGroup);
map.addLayer(clusterGroup);
} else {
map.removeLayer(clusterGroup);
map.addLayer(plainGroup);
}
refresh();
});
map.on('zoomend', refresh);
refresh();
})();
</script>
</body>
</html>
Language: the code and this page are in English; the full tutorial and API notes for this example are in Chinese. The demo itself is the authoritative version — it runs exactly as shown.