经纬志
首页 / 实战手册 / MapLibre 样式与图层

自定义图标全部 404,标记变成空白

先看结论

符号图层加好了,样式里也写了 icon-image,地图上却一个图标都没有, 网络面板里一片红色 404。

最后验证 2026-11-03 · 多技术栈 · 更新 2026-10-29 · 引用本文

现象

控制台报图片加载失败,所有 icon-image 引用都失效:

Uncaught Error: Could not load image because of 404 Not Found.
    at maplibre-gl.js:4211

GET /icons/pin.png 404 (Not Found)
GET /icons/pin@2x.png 404 (Not Found)

有时图片请求是 200,但地图上仍然没有图标,并且没有任何报错。

原因

两类问题混在一起:

  1. 路径问题。资源放在 /public/icons,打包后实际路径是 /assets/icons,但代码里写的是相对路径 ./icons/pin.png,页面在子路由下就变成 404。
  2. 时机问题。用 img.onload 之外的方式调用 addImage,图片还没解码完就注册,注册失败或注册成空图。
  3. 高清屏问题。只提供了 1 倍图,MapLibre 在 pixelRatio 为 2 的设备上找不到 @2x 版本,图标会被跳过。

排查过程

先确认图片本身能否加载,再确认是否已注册到样式:

var img = new Image();
img.onload = function () { console.log('图片尺寸', img.width, img.height); };
img.onerror = function (e) { console.error('图片加载失败', e); };
img.src = '/icons/pin.png';

// 加载完成后检查是否注册成功
console.log('已注册图标', map.hasImage('pin'));

解决方案

用 map.loadImage 拿到 ImageBitmap,再 addImage。 两步都返回 Promise,确保顺序正确:

Promise.all([
  map.loadImage('/icons/pin.png'),
  map.loadImage('/icons/pin@2x.png')
]).then(function (images) {
  map.addImage('pin', images[0].data, { pixelRatio: 1 });
  map.addImage('pin', images[1].data, { pixelRatio: 2 });
}).catch(function (err) {
  console.error('图标注册失败', err);
});

map.addLayer({
  id: 'poi-icons',
  type: 'symbol',
  source: 'poi',
  layout: {
    'icon-image': 'pin',
    'icon-size': 0.8,
    'icon-allow-overlap': true
  }
});

只有一个文件时,用 sdf 或 pixelRatio: 2 让高分屏复用同一张图:

map.loadImage('/icons/pin.png').then(function (image) {
  map.addImage('pin', image.data, { pixelRatio: 2 });
});

资源路径统一走绝对路径,避免子路由下相对路径失效:

// 错误:./icons/pin.png 在 /project/detail 下会解析成 /project/icons/pin.png
// 正确:由构建变量注入基路径
var ICON_BASE = '/assets/icons/';
map.loadImage(ICON_BASE + 'pin.png');

验证

map.hasImage('pin') 为 true; 网络面板不再有 404;地图上每个点都显示图标。 把浏览器切到 2 倍像素比,图标仍然清晰,不出现空白方块。

顺序陷阱:addImage 必须在图层创建之前完成。 图层已经存在时补注册图标不会自动刷新,需要重新设置 icon-image 才会生效。

引用本文

经纬志(2026)。《自定义图标全部 404,标记变成空白》。https://www.jingweizhi.com/cookbook/maplibre-icon-404.html(访问日期 2026-11-03)。