Atlaslog
Atlaslog / Examples / Leaflet / Marker Cluster
Leaflet · Runnable Example

Marker Cluster

Cluster 700 points, split them on zoom, and keep click handlers working.

Open standalone → Chinese notes All Leaflet examples
/demos/leaflet-marker-cluster.htmlLIVE
single HTML file · CDN dependencies onlyinterface may contain Chinese labels

Source

demos/leaflet-marker-cluster.html
<!--
  示例:聚合标记
  要点: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.

More Leaflet examples

Leaflet · EXAMPLEMarker Leaflet · EXAMPLEMax Bounds Leaflet · EXAMPLEMeasure