一、Leaflet 简介
Leaflet 是轻量级、开源、移动端友好的交互式地图 JS 库,体积仅约 39KB,支持瓦片地图、标记、弹窗、线面绘制、图层控制、GeoJSON 等,兼容所有主流浏览器,无第三方依赖。
1. 引入方式
方式 1:CDN 快速引入(推荐开发)
<!-- CSS -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<!-- JS -->
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
方式 2:npm 安装(工程化项目)
npm install leaflet
// Vue/React/原生工程引入
import L from 'leaflet'
import 'leaflet/dist/leaflet.css'
2. 基础容器要求
地图容器必须设置固定宽高,否则地图不渲染:
<div id="map" style="width:100%;height:600px;"></div>
二、地图初始化(核心 API L.map())
基础初始化
// 1. 创建地图实例,绑定容器id
const map = L.map('map', {
center: [39.9042, 116.4074], // 中心点 [纬度,经度] 北京
zoom: 10, // 默认缩放等级 0-18
minZoom: 3, // 最小缩放
maxZoom: 18, // 最大缩放
zoomControl: true, // 显示缩放按钮
attributionControl: true, // 右下角版权信息
scrollWheelZoom: true, // 鼠标滚轮缩放
dragging: true, // 拖拽地图
doubleClickZoom: true // 双击放大
});
常用地图操作方法
// 修改中心点+缩放
map.setView([30.5928, 114.3055], 12);
// 仅修改缩放
map.setZoom(14);
// 获取当前地图状态
const center = map.getCenter(); // {lat,lng}
const zoom = map.getZoom();
// 定位到范围(自动适配缩放)
map.fitBounds([[30,113],[31,115]]);
// 禁用/开启拖拽
map.dragging.disable();
map.dragging.enable();
三、瓦片图层(底图)L.tileLayer
地图必须加载瓦片图层才会显示画面,支持高德、百度、天地图、OpenStreetMap、谷歌等瓦片。
1. 开源 OSM 底图(官方默认)
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors',
subdomains: ['a','b','c'], // 多域名加速
maxZoom: 18
}).addTo(map); // 添加到地图
2. 国内常用瓦片示例
(1)高德标准地图
L.tileLayer('https://{s}.is.autonavi.com/appmaptile?style=7&x={x}&y={y}&z={z}',{
subdomains: ['01','02','03','04'],
attribution: "高德地图"
}).addTo(map)
(2)天地图(需申请 key)
const tdtKey = "你的天地图KEY";
L.tileLayer(`https://t0.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tile&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${tdtKey}`,{
attribution: "天地图"
}).addTo(map)
图层管理
// 保存图层实例方便切换
const osmLayer = L.tileLayer("xxx").addTo(map);
// 移除图层
map.removeLayer(osmLayer);
// 判断图层是否在地图
map.hasLayer(osmLayer);
四、标记 Marker / 图标 Icon
1. 基础点位标记
// 创建标记 [lat,lng]
const marker = L.marker([39.9042, 116.4074]).addTo(map);
// 绑定弹窗(点击弹出)
marker.bindPopup("<b>北京</b><br>首都").openPopup();
// 绑定悬浮提示
marker.bindTooltip("北京市", {permanent:false}).openTooltip();
2. 自定义图标(替换默认大头针)
// 定义图标
const customIcon = L.icon({
iconUrl: 'marker.png', // 图标图片地址
iconSize: [32, 40], // 图标宽高
iconAnchor: [16, 40], // 图标底部锚点(对准坐标点)
popupAnchor: [0, -40], // 弹窗偏移
shadowUrl: 'shadow.png',
shadowSize: [32, 40]
});
// 使用自定义图标
L.marker([39.9,116.4], {icon: customIcon}).addTo(map);
Marker 常用方法
marker.setLatLng([新纬度,新经度]); // 移动标记
marker.getLatLng(); // 获取坐标
marker.unbindPopup(); // 解绑弹窗
marker.remove(); // 删除标记
五、矢量图形:折线、多边形、圆
1. 折线 Polyline(路线)
const linePoints = [
[39.91,116.40],
[39.92,116.42],
[39.90,116.43]
];
const polyline = L.polyline(linePoints, {
color: "#f00", // 线条颜色
weight: 3, // 线条宽度
opacity: 0.8, // 透明度
dashArray: "5,10" // 虚线
}).addTo(map);
polyline.bindPopup("路线");
2. 多边形 Polygon(区域面)
const polygonPoints = [
[39.92,116.38],
[39.90,116.38],
[39.90,116.42],
[39.92,116.42]
];
L.polygon(polygonPoints, {
fillColor: "#08f", // 填充色
fillOpacity: 0.3,
color: "#006",
weight: 2
}).addTo(map);
3. 圆形 Circle
// 圆心 + 半径(米)
L.circle([39.9042,116.4074], {
radius: 1000,
fillColor: "yellow"
}).addTo(map);
六、弹窗 Popup / 提示框 Tooltip
Popup 点击弹窗
// 独立弹窗
const popup = L.popup()
.setLatLng([39.9,116.4])
.setContent("<h3>标题</h3>内容文字")
.openOn(map); // 自动关闭其他弹窗
// 弹窗配置
marker.bindPopup("内容", {
maxWidth: 300,
closeButton: true,
autoPan: true
});
Tooltip 悬浮提示
marker.bindTooltip("常驻文字", {
permanent: true, // 永久显示,不悬浮
direction: "top", // top/left/right/bottom
offset: [0,-10]
});
七、GeoJSON 地理数据加载(最常用业务功能)
GeoJSON 是标准空间数据格式,支持点位、线、面批量渲染。
基础示例
const geojsonData = {
type: "FeatureCollection",
features: [
{
type: "Feature",
geometry: {type:"Point", coordinates:[116.4074,39.9042]}, // 注意:GeoJSON 顺序 [lng,lat]
properties: {name:"北京"}
}
]
};
L.geoJSON(geojsonData, {
// 自定义每个要素样式
style: function(feature) {
return {color:"red", weight:2};
},
// 自定义点位图标
pointToLayer: function(feature, latlng) {
return L.marker(latlng).bindPopup(feature.properties.name);
},
// 要素创建后执行
onEachFeature: function(feature, layer) {
layer.bindPopup(feature.properties.name);
}
}).addTo(map);
加载外部 geojson 文件
fetch("area.geojson")
.then(res=>res.json())
.then(data=>{
L.geoJSON(data).addTo(map);
})
八、图层组与图层控制器(多底图切换)
1. 图层组 LayerGroup(批量管理标记 / 图形)
// 创建图层组
const markerGroup = L.layerGroup();
// 添加多个标记到组
const m1 = L.marker([39.9,116.4]);
const m2 = L.marker([39.91,116.41]);
markerGroup.addLayer(m1).addLayer(m2);
markerGroup.addTo(map);
// 批量清空
markerGroup.clearLayers();
2. 图层切换控制器 L.control.layers
// 底图图层(单选)
const baseMaps = {
"标准地图": osmLayer,
"高德地图": amapLayer
};
// 覆盖图层(多选,标记/区域)
const overlayMaps = {
"点位": markerGroup,
"行政区": geoLayer
};
// 添加切换控件到右上角
L.control.layers(baseMaps, overlayMaps).addTo(map);
九、地图事件监听(点击、缩放、移动)
1. 地图全局事件
// 点击地图任意位置
map.on('click', function(e) {
console.log("点击坐标:", e.latlng.lat, e.latlng.lng);
// 在点击处创建弹窗
L.popup()
.setLatLng(e.latlng)
.setContent(`纬度:${e.latlng.lat}<br>经度:${e.latlng.lng}`)
.openOn(map);
});
// 缩放结束
map.on('zoomend', ()=>{
console.log("当前缩放等级", map.getZoom())
})
// 地图拖动结束
map.on('moveend', ()=>{
console.log("中心点", map.getCenter())
})
// 移除事件监听
map.off('click');
2. 标记 / 图层事件
marker.on('mouseover', ()=>{
marker.openTooltip();
})
marker.on('click', ()=>{
alert("点击标记");
})
十、坐标转换工具 & 常用工具函数
1. 坐标转换
// 经纬度转地图像素
const point = map.latLngToContainerPoint([39.9,116.4]);
// 像素转经纬度
const latlng = map.containerPointToLatLng([200,200]);
2. 计算两点距离(米)
const p1 = L.latLng(39.9,116.4);
16.4);
const p2 = L.latLng(39.91,116.41);
const distance = p1.distanceTo(p2);
console.log("距离米数", distance);
3. 获取当前地图可视范围
const bounds = map.getBounds();
// 西南角、东北角
console.log(bounds.getSouthWest(), bounds.getNorthEast());
十一、控件:比例尺、版权、缩放
// 比例尺控件(左下角)
L.control.scale({
imperial: false, // 不显示英里
metric: true // 显示公里/米
}).addTo(map);
// 自定义版权文字
map.attributionControl.setPrefix("我的地图系统");
十二、完整可运行 Demo(复制直接打开)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Leaflet 完整示例</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<style>#map{width:100%;height:600px;}</style>
</head>
<body>
<div id="map"></div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
// 1. 初始化地图
const map = L.map('map',{
center:[30.5928,114.3055],
zoom:12
});
// 2. 加载OSM底图
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{
attribution:'© OSM'
}).addTo(map);
// 3. 添加标记
const marker = L.marker([30.5928,114.3055]).addTo(map);
marker.bindPopup("<b>武汉市</b>").openPopup();
marker.bindTooltip("武汉",{permanent:true});
// 4. 绘制圆形范围
L.circle([30.5928,114.3055],{radius:2000,fillColor:'blue',fillOpacity:0.2}).addTo(map);
// 5. 比例尺控件
L.control.scale().addTo(map);
// 6. 点击地图获取坐标
map.on('click',e=>{
L.popup()
.setLatLng(e.latlng)
.setContent(`纬度:${e.latlng.lat.toFixed(4)}<br>经度:${e.latlng.lng.toFixed(4)}`)
.openOn(map);
})
</script>
</body>
</html>
十三、常见问题解决
- 地图空白不显示
- #map 容器必须设置 height;
- 确认瓦片地址可访问、网络无跨域;
- 自定义图标不显示
- iconAnchor 锚点设置正确,图片路径无误;
- 国内瓦片偏移
- Leaflet 默认 WGS84 坐标;高德 / 百度是 GCJ02、BD09,需要坐标转换插件
leaflet-chs-proj;
- Leaflet 默认 WGS84 坐标;高德 / 百度是 GCJ02、BD09,需要坐标转换插件
- 弹窗被截断
- popup 设置
maxWidth:500加宽弹窗;
- popup 设置
- 移动端拖拽卡顿
- 关闭多余图层,降低矢量面数量,使用图层分组统一渲染。
十四、扩展插件推荐
leaflet-draw:地图绘制工具(点线面编辑)leaflet-search:地点检索leaflet.chinatmsproviders:国内高德 / 百度 / 天地图瓦片封装leaflet.markercluster:海量标记聚合(千级点位不卡顿)leaflet-routing-machine:路线规划