经纬志
首页 / Leaflet / 教程

Marker 与自定义图标

先看结论

标记是 Leaflet 里唯一用 DOM 绘制的图层,这让它可以放任意 HTML, 也让它在几千个以上时成为性能瓶颈。图标本身是一个 L.Icon 实例, L.icon 只是它的工厂函数。

最后验证 2026-11-03 · Leaflet 1.9.4 · 更新 2026-10-29 · 引用本文

默认图标为什么经常不显示

默认图标不写死在 JS 里,而是按一个相对路径去拼图片地址。 一旦把 leaflet.js 从 CDN 引入、把 CSS 放在别处、或者被构建工具改写了目录结构, 拼出来的地址就是 404,画布上只留下一个不可见的点击区域。 Marker 元素仍然会创建,所以控制台不会报错。

<link rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.css">
<script src="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
  // 显式告诉 Leaflet 图标资源目录,避免相对路径拼错
  L.Icon.Default.imagePath = 'https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/images/';

  var map = L.map('map').setView([39.907359, 116.391263], 12);
</script>

imagePath 只影响默认图标。自定义图标一律写完整 URL, 不受这个设置影响,也不会因为部署目录变化而失效。

L.icon 的六个参数

参数含义常见错误
iconUrl图片地址,必填写成相对路径,部署后 404
iconSize图标的显示尺寸 [宽, 高]漏写则按图片原始像素渲染,大图会撑破布局
iconAnchor图标内哪个点对准坐标漏写默认以左上角对准,视觉上整体偏移半个图标
popupAnchor弹窗相对锚点的偏移不设会盖住图标本身
shadowUrl阴影图片2 倍图配 1 倍阴影,比例不对
className附加到图标元素的类名用它做 CSS 动画时忘了处理 transform
var pin = L.icon({
  iconUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png',
  shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
  iconSize:    [25, 41],
  iconAnchor:  [12, 41],   // 底部尖端对准坐标
  popupAnchor: [1, -34],
  shadowSize:  [41, 41]
});

L.marker([39.907359, 116.391263], { icon: pin })
  .bindPopup('天安门')
  .addTo(map);

iconAnchor 是最容易被忽略的参数。标记的坐标语义是「图上这一个点」, 而图片是一个矩形,必须指定矩形内的哪个位置代表那个点。 对图钉类图标,答案通常是底部尖端;对圆形头像,是中心。

divIcon:用 HTML 画图标

需要显示数字、状态、头像或纯 CSS 图形时,用 L.divIcon。 它不加载图片,直接插入一个 div,内容与样式全部自己控制。

function numberIcon(n, color) {
  return L.divIcon({
    className: 'num-pin',
    html: '<span style="background:' + color + '">' + n + '</span>',
    iconSize: [26, 26],
    iconAnchor: [13, 13]
  });
}

L.marker([39.916345, 116.397155], { icon: numberIcon(1, '#c2410c') }).addTo(map);
L.marker([39.899000, 116.407000], { icon: numberIcon(2, '#17694a') }).addTo(map);

divIcon 的内容会当 HTML 插入。数字、颜色这类自己生成的值可以拼字符串, 来自用户输入的文本必须先转义,否则等于在地图里开了一个 XSS 入口。

旋转与交互选项

1.9 的标记支持 rotationAngle 与 rotateOrigin, 用于风向标、车头朝向这类场景。它们作用在图标元素上,不影响坐标计算。

选项默认作用
draggablefalse允许拖动,配合 dragend 取新坐标
keyboardtrue可用 Tab 聚焦、回车触发点击
riseOnHoverfalse悬停时提升到同 pane 最上层
zIndexOffset0同一 pane 内的叠放微调
opacity1整体透明度
var m = L.marker([39.907359, 116.391263], {
  draggable: true,
  riseOnHover: true,
  rotationAngle: 30,
  rotateOrigin: [12, 41]
}).addTo(map);

m.on('dragend', function () {
  var p = m.getLatLng();
  console.log(p.lat.toFixed(6), p.lng.toFixed(6));
});

注意事项

用 Marker 承载上万个点

每个 Marker 都是一个真实 DOM 节点,加上事件监听与阴影, 三千个左右就开始明显掉帧。点图层超过这个量级,改用 L.circleMarker 配 canvas 渲染器,或者做聚合,见「性能与大量点」。

图标尺寸与高分屏

iconSize 是 CSS 像素。准备了 2 倍图时,尺寸仍按设计稿的 CSS 尺寸写, 例如 48 像素的图片配 iconSize: [24, 24]。 直接把 48 写进尺寸会让图标看起来大一倍。

拖动后坐标是浮点数

dragend 拿到的经纬度有十几位小数。入库或展示前按业务精度取整, 否则同一位置反复拖动会生成大量实质重复的记录。

引用本文

经纬志(2026)。《Marker 与自定义图标》。https://www.jingweizhi.com/leaflet/tutorial/markers-icons.html(访问日期 2026-11-03)。