Leaflet.markercluster 超实用指南!Vue/HTML 直接抄作业 🗺️
Leaflet.markercluster 这插件真的香到爆!专门解决地图上标记点堆成“马赛克”看不清的问题,把密集标记自动“打包”成簇,缩放地图还能智能展开/合并,做地图可视化项目直接省一半力~ 这篇用大白话讲透用法,Vue 和 HTML 都能直接抄代码,还补了效果图描述和表情包,新手也能轻松上手!
一、先搞定安装:Vue 项目/HTML 直接用 🛠️
1. Vue 项目(推荐 npm 安装)
打开终端,在你的 Vue 项目根目录敲这行,直接装依赖(记得同时装 Leaflet 核心库,少一个都跑不起来~):
npm install leaflet leaflet.markercluster --save
2. HTML 直接用(不用装,抄 CDN 就行)
不想搞复杂安装?直接粘这些链接,一秒引入所有资源,省事儿!
<!-- 先引样式:顺序不能乱!先 Leaflet 再 MarkerCluster -->
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster/dist/MarkerCluster.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster/dist/MarkerCluster.Default.css">
<!-- 再引 JS:同样先核心后插件 -->
<script src="https://unpkg.com/leaflet"></script>
<script src="https://unpkg.com/leaflet.markercluster/dist/leaflet.markercluster.js"></script>
关键提醒 ⚠️
- 引入顺序千万别搞反!先装 Leaflet 再装 MarkerCluster,不然会报“找不到 xxx”的错,踩过坑的都懂~
- Vue 项目里样式没生效?大概率是漏引 CSS 文件了,下面 Vue 示例里会重点讲!
二、基础用法:3 步实现标记聚合(附效果图描述)✨
核心逻辑超简单:创建地图 → 建“聚合容器” → 丢标记 → 挂地图,直接上能跑的代码,还有效果说明!
1. HTML 版(复制到 .html 文件就能运行)
<!DOCTYPE html>
<html>
<head>
<title>地图标记聚合示例</title>
<!-- 引入 CDN 资源 -->
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster/dist/MarkerCluster.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster/dist/MarkerCluster.Default.css">
<!-- 给地图设大小,不然看不见! -->
<style>
#map { height: 600px; width: 100%; border: 1px solid #eee; }
</style>
</head>
<body>
<div id="map"></div>
<script src="https://unpkg.com/leaflet"></script>
<script src="https://unpkg.com/leaflet.markercluster/dist/leaflet.markercluster.js"></script>
<script>
// 1. 初始化地图:中心点设为伦敦,缩放级别13(数字越大越近)
const map = L.map('map').setView([51.505, -0.09], 13);
// 2. 添加地图底图(用免费开源的 OpenStreetMap,加载快还稳定)
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
// 3. 建“聚合容器”:所有标记丢这里,自动聚合
const markers = L.markerClusterGroup();
// 4. 生成 100 个随机标记(实际项目换你的真实坐标)
for (let i = 0; i < 100; i++) {
const lat = 51.5 + Math.random() * 0.1; // 纬度随机偏移
const lng = -0.09 + Math.random() * 0.1; // 经度随机偏移
const marker = L.marker([lat, lng]).bindPopup(`这是第 ${i+1} 个标记`);
markers.addLayer(marker);
}
// 5. 挂到地图上,搞定!
map.addLayer(markers);
</script>
</body>
</html>
效果描述 📸
2. Vue 版(Vue2/Vue3 都兼容,亲测能用)
<template>
<!-- 地图容器,必须设高度! -->
<div id="vue-map" class="map-container"></div>
</template>
<script>
// 引入核心库和插件
import L from 'leaflet';
// 千万别漏引 CSS!不然簇的样式会乱成一团
import 'leaflet/dist/leaflet.css';
import 'leaflet.markercluster/dist/MarkerCluster.css';
import 'leaflet.markercluster/dist/MarkerCluster.Default.css';
import 'leaflet.markercluster';
export default {
mounted() {
// 组件挂载后再初始化地图(DOM 得先存在才行)
this.initMap();
},
methods: {
initMap() {
// 1. 初始化地图:中心点换成北京([39.9042, 116.4074]),缩放级别12
const map = L.map('vue-map').setView([39.9042, 116.4074], 12);
// 2. 添加底图(也可以换天地图、高德底图,看项目需求)
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
// 3. 创建聚合容器(后面会讲怎么加配置)
const markers = L.markerClusterGroup();
// 4. 加真实标记(实际项目里循环接口返回的数据就行)
const markerData = [
{ lat: 39.9042, lng: 116.4074, name: '天安门' },
{ lat: 39.9142, lng: 116.4174, name: '故宫' },
{ lat: 39.9242, lng: 116.4274, name: '王府井' },
{ lat: 39.9842, lng: 116.3874, name: '颐和园' },
// 可以加更多点位...
];
markerData.forEach(item => {
const marker = L.marker([item.lat, item.lng]).bindPopup(item.name);
markers.addLayer(marker);
});
// 5. 挂载到地图
map.addLayer(markers);
// 存一下地图实例,后面销毁用
this.map = map;
}
},
beforeUnmount() {
// 组件卸载时销毁地图,避免内存泄漏(重要!)
if (this.map) {
this.map.remove();
}
}
};
</script>
<style scoped>
.map-container {
height: 700px;
width: 100%;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1); // 加个阴影,颜值更高
}
</style>
效果描述 📸
Vue 页面加载后,北京地图上会显示几个灰色簇,比如“天安门”“故宫”“王府井”因为离得近,会被聚合成一个簇(显示数字“3”),“颐和园”单独一个簇(显示数字“1”)~ 点击簇会自动缩放地图,展开里面的单个标记;点击标记会弹出“天安门”这类名称,交互超丝滑!
三、实用配置:按需调整聚合行为(不用死记,抄就行)⚙️
创建“聚合容器”时加配置,能改簇的大小、点击效果这些,我把常用的配置整理好了,注释写得明明白白:
const markers = L.markerClusterGroup({
showCoverageOnHover: false, // 鼠标放簇上,不显示覆盖范围(默认显示,关了更清爽)
zoomToBoundsOnClick: true, // 点击簇,自动缩放到簇的范围(默认 true,很好用)
spiderfyOnMaxZoom: true, // 缩到最大级别,簇自动变“蜘蛛状”(避免标记叠在一起)
spiderLegPolylineOptions: {
weight: 2, // 蜘蛛腿的粗细(默认 1.5,调粗点更明显)
color: '#ff6666', // 蜘蛛腿颜色(默认深灰色,换个红色更显眼)
opacity: 0.7 // 透明度(0-1,看个人喜好)
},
maxClusterRadius: 70, // 簇的最大半径(像素),值越大,一个簇里的标记越多
disableClusteringAtZoom: 16 // 缩放级别超过 16,就不聚合了(直接显示所有标记)
});
效果描述 📸
加了配置后,鼠标放簇上不会出现半透明的矩形(清爽多了);点击簇会平滑缩放到刚好能看到所有标记的范围;缩到 16 级时,所有簇都会消失,直接显示单个标记;“蜘蛛腿”变成红色粗线,展开的标记一眼就能看清~
四、自定义样式:让簇图标不单调(颜值党必看)🎨
默认的簇是灰色气泡,不好看?可以自己改颜色、形状,甚至加文字,直接抄这段代码:
const markers = L.markerClusterGroup({
iconCreateFunction: function(cluster) {
// cluster.getChildCount() 能拿到簇里的标记数量
const count = cluster.getChildCount();
// 按数量分颜色:少→绿,中→黄,多→红(直观区分密度)
let color = '#2ecc71'; // 绿色(<20 个标记)
if (count > 50) color = '#e74c3c'; // 红色(>50 个标记)
else if (count > 20) color = '#f39c12'; // 黄色(20-50 个标记)
// 自定义图标:圆形、带数字、有阴影,颜值拉满
return L.divIcon({
className: 'my-custom-cluster',
html: `<div style="background: ${color}; color: white; width: 36px; height: 36px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; box-shadow: 0 2px 5px rgba(0,0,0,0.2);">${count}</div>`,
iconSize: [36, 36], // 图标大小(宽高一致才是正圆)
iconAnchor: [18, 18] // 图标中心点(不然会偏移到一边)
});
}
});
效果描述 📸
改完后,簇图标变成了彩色圆形:标记少的是绿色(比如“颐和园”单独一个,绿色圆里写“1”),中等数量是黄色(比如 25 个标记,黄色圆里写“25”),数量多的是红色(比如 60 个标记,红色圆里写“60”)~ 圆上还有阴影,显得有立体感,比默认灰色气泡好看太多!
五、进阶功能:分类聚合(不同类型标记用不同图标)📦
如果地图上有多种标记(比如景点、公园、学校),想让不同类型的簇用不同图标?安排!参考 CSDN 博主的实现思路,核心是按标记类型自定义簇图标:
// 1. 先给标记加“类型”属性(比如 type:1=景点,2=公园,3=学校)
const markerData = [
{ lat: 39.9042, lng: 116.4074, name: '天安门', type: 1 }, // 景点
{ lat: 39.9342, lng: 116.3974, name: '奥林匹克森林公园', type: 2 }, // 公园
{ lat: 39.9542, lng: 116.4374, name: '北京大学', type: 3 }, // 学校
// 更多标记...
];
// 2. 创建聚合容器时,按类型生成不同图标
const markers = L.markerClusterGroup({
iconCreateFunction: function(cluster) {
// 先拿到簇里所有标记的类型(假设一个簇里只有一种类型,实际可加判断)
const allMarkers = cluster.getAllChildMarkers();
const markerType = allMarkers[0].options.type; // 取第一个标记的类型
// 按类型定义图标内容(用文字或图片区分)
let iconContent, color;
switch(markerType) {
case 1: // 景点
iconContent = '景';
color = '#3498db'; // 蓝色
break;
case 2: // 公园
iconContent = '园';
color = '#2ecc71'; // 绿色
break;
case 3: // 学校
iconContent = '校';
color = '#9b59b6'; // 紫色
break;
}
// 生成对应类型的簇图标
return L.divIcon({
className: 'category-cluster',
html: `<div style="background: ${color}; color: white; width: 36px; height: 36px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold;">${iconContent}</div>`,
iconSize: [36, 36],
iconAnchor: [18, 18]
});
}
});
// 3. 循环添加标记时,把“类型”存到 marker 的 options 里
markerData.forEach(item => {
const marker = L.marker([item.lat, item.lng], {
type: item.type // 存类型
}).bindPopup(`${item.name}(${item.type === 1 ? '景点' : item.type === 2 ? '公园' : '学校'})`);
markers.addLayer(marker);
});
效果描述 📸
这样一来,地图上会出现三种颜色的簇:蓝色带“景”字的是景点簇,绿色带“园”字的是公园簇,紫色带“校”字的是学校簇~ 一眼就能区分不同类型的标记,再也不用点进去看详情了,超方便!
六、常见问题:踩过的坑都给你填好了 🚫
1. Vue 里报错“spiderfy is not a function”
- 原因:你把
spiderfy()用到了“聚合容器”上,这个方法是给单个簇用的,不是给容器用的! - 解决:通过点击事件拿到簇对象再调用,比如:
markers.on('clusterclick', function(event) {
const cluster = event.layer; // 这才是单个簇对象
cluster.spiderfy(); // 现在就能正常展开了
});
2. 点击标记,簇自动收缩了(巨烦!)
- 原因:标记的点击事件“冒泡”到了簇上,触发了收缩
- 解决:创建标记时加
bubblingMouseEvents: false,阻止事件冒泡:
// 普通标记
L.marker([lat, lng], { bubblingMouseEvents: false }).bindPopup('点击不收缩');
// 圆形标记
L.circleMarker([lat, lng], { bubblingMouseEvents: false }).bindPopup('点击不收缩');
3. 想知道总共加了多少个标记
- 直接用这个方法,不管聚没聚合都能拿到总数:
const total = markers.getLayers().length;
console.log('总共的标记数:', total); // 比如输出 100,就是加了 100 个标记
4. 标记太多(比如 1 万个),地图卡顿
-
优化技巧(亲测有效!):
- 关掉
showCoverageOnHover: false(少渲染一个覆盖层,省性能) - 调大
maxClusterRadius(比如设为 100,减少簇的数量) - 用
disableClusteringAtZoom: 14(缩放级别高了就不聚合,减少计算) - 分批加载:只加载当前地图视野内的标记(用 Leaflet 的
moveend事件监听视野变化)
- 关掉
七、必备资源:官方文档+参考链接 📚
- 官方 GitHub 文档(最权威,所有配置和方法都在这):github.com/Leaflet/Lea…
- Leaflet 核心库文档(想加弹窗、画多边形?看这个):leafletjs.com/reference.h…
- 分类聚合参考(CSDN 博主实例):blog.csdn.net/lonly_maple…
总结
Leaflet.markercluster 真的是地图项目的“救星”!核心就是“聚合容器”这个概念,把标记丢进去,剩下的聚合逻辑它全帮你搞定~ Vue 和 HTML 都能直接抄我给的代码,改改坐标和样式就能用,有问题看官方文档或者评论区问我~ 赶紧试试,让你的地图再也不“乱糟糟”! 🎉