Leaflet.js 完整实用使用手册

一、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: '&copy; 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);

复制代码
// 独立弹窗
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:'&copy; 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>

十三、常见问题解决

  1. 地图空白不显示
    • #map 容器必须设置 height;
    • 确认瓦片地址可访问、网络无跨域;
  2. 自定义图标不显示
    • iconAnchor 锚点设置正确,图片路径无误;
  3. 国内瓦片偏移
    • Leaflet 默认 WGS84 坐标;高德 / 百度是 GCJ02、BD09,需要坐标转换插件 leaflet-chs-proj
  4. 弹窗被截断
    • popup 设置 maxWidth:500 加宽弹窗;
  5. 移动端拖拽卡顿
    • 关闭多余图层,降低矢量面数量,使用图层分组统一渲染。

十四、扩展插件推荐

  1. leaflet-draw:地图绘制工具(点线面编辑)
  2. leaflet-search:地点检索
  3. leaflet.chinatmsproviders:国内高德 / 百度 / 天地图瓦片封装
  4. leaflet.markercluster:海量标记聚合(千级点位不卡顿)
  5. leaflet-routing-machine:路线规划