两层半透明叠在一起,颜色比预期深了一倍
两个 50% 透明的图层叠上去,重叠区看起来像 75% 不透明,设计稿完全对不上。
现象
蓝色面图层 fill-opacity: 0.5,红色面图层 fill-opacity: 0.5。
重叠区域显示为暗紫色,整体亮度明显低于任一单层,
与设计稿标注的「两层各 50%,叠加后仍能看清底图」不符。
用取色器量:单层区域 #7fa8d8,重叠区 #6c7ab0,
明度下降超过 20%。
原因
透明度不是相加,而是按 source-over 合成:
α_result = α_top + α_bottom × (1 - α_top)
α = 0.5 + 0.5 × 0.5 = 0.75
两层各 50% 后,重叠区实际不透明度是 75%,不是 50%。
如果图层容器还设置了 CSS opacity,它会在合成之后再乘一次:
0.75 × 0.8 = 0.6。
Leaflet 的 opacity 选项作用于整个 pane(CSS opacity),
fillOpacity 作用于单个路径,两者会相乘。
| fill-opacity | layer opacity | 实际不透明度 |
|---|---|---|
| 0.5 | 1.0 | 0.5 |
| 0.5 | 0.8 | 0.4 |
| 0.5 + 0.5 两层 | 1.0 | 0.75 |
| 0.5 + 0.5 两层 | 0.8 | 0.6 |
排查过程
- 在 Elements 面板看面图层所在 pane 的
opacity样式值,是否小于 1。 - 看路径的
fill-opacity属性,与图层选项是否重复设置。 - 用取色器量单层与重叠区的实际色值,反算不透明度。
- 检查是否有全局 CSS 对
.leaflet-overlay-pane svg设置了opacity。
const pane = map.getPane('overlayPane');
console.log(getComputedStyle(pane).opacity); // 0.8 <- 额外的乘数
console.log(getComputedStyle(pane.querySelector('path')).fillOpacity); // 0.5
解决方案
只在一个地方控制透明度。需要看穿底图时降低 fillOpacity,
不要同时设图层 opacity。
// 正确:只设 fillOpacity
L.geoJSON(blue, {
style: { fillColor: '#1a6fd4', fillOpacity: 0.35, weight: 1, opacity: 0.9 }
}).addTo(map);
// 若必须叠加两层并保持视觉一致,把单层透明度按合成公式反算
// 目标重叠区 0.5,则单层 α 满足 2α - α² = 0.5 -> α ≈ 0.293
L.geoJSON(red, {
style: { fillColor: '#e8590c', fillOpacity: 0.293, weight: 1 }
}).addTo(map);
Mapbox GL 中 fill-opacity 与图层 opacity 同样是相乘关系,
用 ['*', 0.5, ['get','alpha']] 这类表达式时要注意最终的乘积。
map.setPaintProperty('area-blue', 'fill-opacity', 0.35);
map.setLayoutProperty('area-blue', 'visibility', 'visible');
map.setPaintProperty('area-blue', 'fill-color', '#1a6fd4');
// 不要再用 map.setPaintProperty('area-blue', 'fill-opacity-transition', ...) 叠 opacity
验证
单层区域与重叠区的色差不超过 8%,取色器读出的重叠区色值应接近设计稿; pane 的 computed opacity 为 1。
console.assert(getComputedStyle(map.getPane('overlayPane')).opacity === '1', '仍有额外透明度');
// 色差检查:用 canvas 取两点像素比较
const ctx = document.querySelector('canvas').getContext('2d');
console.assert(Math.abs(ctx.getImageData(320, 240, 1, 1).data[0] -
ctx.getImageData(520, 240, 1, 1).data[0]) < 20, '色差过大');
适用版本
| 库 | 版本 | 说明 |
|---|---|---|
| Leaflet | 1.9.x | opacity 作用于 pane,fillOpacity 作用于路径 |
| Mapbox GL JS | 2.15 / 3.x | fill-opacity 与 layer opacity 相乘 |
| CSS 合成 | source-over | α = α_top + α_bottom(1 - α_top) |
经纬志(2026)。《两层半透明叠在一起,颜色比预期深了一倍》。https://www.jingweizhi.com/cookbook/opacity-multiply.html(访问日期 2026-11-03)。