经纬志
首页 / 实战手册 / 样式与渲染

线宽不随缩放变化,放大后像一根头发丝

先看结论

12 级时道路像主干道,17 级时同样的道路细得几乎看不见。

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

现象

道路线样式 weight: 3。在 12 级,3 像素约等于实地 40 米,看起来像条主干道; 到 17 级,3 像素只相当于 2.4 米,细到几乎不可见,且与两侧建筑比例完全失调。

反过来,如果为了在高倍看清而把 weight 设成 12, 12 级时道路会粗成一条色带,把整个街区盖住。

原因

Leaflet 的 weight 单位是 屏幕像素,不随 zoom 变化。 而在墨卡托投影下,每像素对应的实地长度随级别减半:

17 级:156543.03 / 2^17 ≈ 1.19 米/像素
12 级:156543.03 / 2^12 ≈ 38.22 米/像素

因此同样的 3 像素,在 12 级代表 115 米,在 17 级只代表 3.6 米。 想让线宽在视觉上稳定,必须让像素宽度随 zoom 递减。

缩放级别米/像素3 px 对应实地
10152.87458 m
1238.22115 m
154.7814 m
180.601.8 m

排查过程

  1. 在不同级别截图,量同一段道路的像素宽度,确认是否为常数。
  2. 看样式配置里 weight 是否写死。
  3. 检查是否有 zoomend 监听去重置 weight,若有则确认公式是否正确。
  4. 对比底图道路宽度,判断目标比例:通常道路在图上的宽度应占实际宽度的 1/5 左右。
console.log(156543.03392 / Math.pow(2, 12));   // 38.22
console.log(156543.03392 / Math.pow(2, 17));   // 1.19

解决方案

Leaflet 用 zoomend 动态设置 weight,或用 SVG 的 vector-effect: non-scaling-stroke 保持实地宽度(不常用)。 Mapbox GL 直接写 interpolate。

// Leaflet:按级别给权重,越放大越细
function weightFor(z) {
  if (z <= 10) return 1;
  if (z <= 13) return 2;
  if (z <= 16) return 3;
  return 5;
}

const roads = L.geoJSON(roadData, {
  style: f => ({ color: '#e8590c', weight: weightFor(map.getZoom()) })
}).addTo(map);

map.on('zoomend', () => roads.setStyle(
  f => ({ color: '#e8590c', weight: weightFor(map.getZoom()) })
));
// Mapbox GL:表达式插值,逐级过渡
map.addLayer({
  id: 'roads',
  type: 'line',
  source: 'roads',
  paint: {
    'line-color': '#e8590c',
    'line-width': ['interpolate', ['exponential', 1.5], ['zoom'],
      8, 0.8,
      12, 1.6,
      16, 4,
      18, 7
    ]
  }
});

验证

在 12 级与 17 级各截一张图,道路屏幕宽度分别为约 2 像素与 4 像素, 与底图道路的实际宽度比例接近;缩小时不出现色带糊满街区。

console.assert(weightFor(12) < weightFor(17), '线宽未随级别增加');
// 实地宽度换算
[12, 17].forEach(z => console.log(z, (156543.03392 / 2 ** z * weightFor(z)).toFixed(1) + ' m'));

适用版本

库版本单位
Leaflet1.9.xweight 为屏幕像素
Mapbox GL JS2.15 / 3.xline-width 为屏幕像素,支持 interpolate
OpenLayers9.xstroke.width 为像素,随分辨率变化需自算
引用本文

经纬志(2026)。《线宽不随缩放变化,放大后像一根头发丝》。https://www.jingweizhi.com/cookbook/line-width-zoom-interpolate.html(访问日期 2026-11-03)。