你有没有遇到过这样的困惑:设计稿里明明是同一个半透明色,换个背景就完全变了样?为什么 50% 透明的红色,放在白色背景上看起来是粉色,放在黑色背景上却是暗红色?本文带你彻底搞懂背后的数学原理——Alpha 混合公式(Alpha Compositing)。
一、核心公式
最终颜色 = 前景色 × α + 背景色 × (1 - α)
这个看似简单的公式,是整个计算机图形学中处理半透明的基石。无论是 CSS 的 rgba()、PS 的图层透明度,还是游戏引擎里的粒子效果,底层都在反复运行它。
符号含义
| 符号 | 含义 |
|---|---|
| 前景色 | 你设置的那个半透明颜色(上层) |
| 背景色 | 它下面那一层的颜色 |
| α(alpha) | 透明度,取值 0~1(0 = 全透明,1 = 不透明) |
| 最终颜色 | 你眼睛实际看到的颜色 |
二、用生活例子秒懂
想象你拿了一张半透明的红色玻璃纸 🟥:
- 把它盖在白纸上 → 看起来是 粉红色 🌸
- 把它盖在黑纸上 → 看起来是 暗红色 🩸
- 把它盖在蓝纸上 → 看起来是 紫色 💜
玻璃纸本身的颜色没变,但你看到的颜色变了。
因为你看到的从来不是玻璃纸本身的颜色,而是"玻璃纸颜色"和"下面那张纸的颜色"按比例混合后的结果。
这个"按比例混合"的比例,就是 α 控制的。
三、为什么是 α 和 (1 - α)?
可以理解为一个分配权重的游戏:
前景色占比 : α (越大越不透明,越显示前景)
背景色占比 : 1 - α (剩下的部分,分配给背景)
─────────────────────
总和 = 1 (保证颜色不溢出、不丢失)
举例说明
当 α = 0.8(80% 不透明)时:
┌─────────────────┬─────────────────┐
│ 前景色 80% │ 背景色 20% │
└─────────────────┴─────────────────┘
当 α = 0.3(30% 不透明)时:
┌──────┬─────────────────────────────┐
│ 30% │ 背景色 70% │
└──────┴─────────────────────────────┘
α 值越小,背景色的"话语权"越大,透明度越高。
四、实战计算示例
示例 1:红色 50% 透明 + 白色背景
- 前景色
#FF0000→ RGB (255, 0, 0) - 背景色
#FFFFFF→ RGB (255, 255, 255) - α = 0.5
分别计算 R、G、B 三个通道:
R 通道: 255 × 0.5 + 255 × 0.5 = 127.5 + 127.5 = 255
G 通道: 0 × 0.5 + 255 × 0.5 = 0 + 127.5 = 127.5
B 通道: 0 × 0.5 + 255 × 0.5 = 0 + 127.5 = 127.5
最终颜色 ≈ rgb(255, 128, 128) = #FF8080 → 粉色 🌸
示例 2:同样的红色 50% 透明 + 黑色背景
- 前景色
#FF0000→ RGB (255, 0, 0) - 背景色
#000000→ RGB (0, 0, 0) - α = 0.5
R 通道: 255 × 0.5 + 0 × 0.5 = 127.5
G 通道: 0 × 0.5 + 0 × 0.5 = 0
B 通道: 0 × 0.5 + 0 × 0.5 = 0
最终颜色 ≈ rgb(128, 0, 0) = #800000 → 暗红色 🩸
示例 3:同样的红色 50% 透明 + 蓝色背景
- 前景色
#FF0000→ RGB (255, 0, 0) - 背景色
#0000FF→ RGB (0, 0, 255) - α = 0.5
R 通道: 255 × 0.5 + 0 × 0.5 = 127.5
G 通道: 0 × 0.5 + 0 × 0.5 = 0
B 通道: 0 × 0.5 + 255 × 0.5 = 127.5
最终颜色 ≈ rgb(128, 0, 128) = #800080 → 紫色 💜
五、三个关键结论
通过上面的例子,可以提炼出三条核心规律:
1. 半透明颜色不是"独立"存在的
它的显示效果永远依赖背后是什么颜色。同一个半透明色,背景变了,呈现效果就变。
2. α 是"混合比例的开关"
α → 1:越接近不透明,越显示前景色
α → 0:越接近全透明,越显示背景色
α = 0.5:前景和背景各占一半
3. 每个 RGB 通道独立计算
红、绿、蓝三个通道分别做一次混合运算,再组合成最终颜色。这也是为什么混合结果有时会"出人意料"——因为你需要同时思考三个通道的变化。
六、这个原理能解决什么实际问题?
问题 1:为什么设计稿上的颜色"搬"到页面就变色了?
原因:设计稿的画布背景可能是白色,而你页面的实际背景是深色。同一个半透明色在两种环境下混合出的结果完全不同。
解决方案:
- 和设计师确认最终的背景色
- 或者让设计师给出不透明的等效色(计算后的最终颜色)
- 或者直接复制设计稿的"合成后"颜色值
问题 2:为什么多层半透明叠加后颜色越来越奇怪?
原因:Alpha 混合是逐层递归的。第一层混合完的结果,又会作为"背景色"参与第二层混合,每多一层误差就被放大一次。
解决方案:能用一层表达的,就不要用多层叠加。
问题 3:为什么黑色半透明做蒙层最"百搭"?
原因:黑色 RGB 是 (0, 0, 0),在公式中"前景色 × α"那部分直接变成 0,相当于只对背景色做了一次"等比例压暗"。所以不管原背景是什么颜色,都只会变暗,而不会偏色。
这也是为什么 #00000080(50% 黑)、#00000099(60% 黑)成为最常用的蒙层色。
问题 4:为什么白色半透明做"提亮"也很常见?
原因:与黑色相反,白色 RGB 是 (255, 255, 255),叠加后相当于让背景色整体"向白色靠拢",产生一种"雾化""柔和"的效果,不会偏色。常用于毛玻璃底色、hover 高亮等场景。
七、拓展:预乘 Alpha(Premultiplied Alpha)
在更底层的图形渲染中,还有一个进阶概念叫 预乘 Alpha。它把公式改写成:
最终颜色 = 前景色' + 背景色 × (1 - α)
其中:前景色' = 前景色 × α (提前算好)
好处:
- 多层混合时性能更好(减少重复乘法)
- 避免边缘"黑边"/"白边"问题(尤其是带抗锯齿的图像)
Canvas、WebGL、游戏引擎、视频合成软件里经常见到这个优化。日常 CSS 开发不用关心,但看到这个名词时别懵。
八、一张图总结
┌─────────────────────────────────────────────────┐
│ │
│ 前景色 (你设的半透明色) │
│ │ │
│ ├──── × α ──────┐ │
│ │ │ │
│ │ ▼ │
│ │ ┌──────────┐ │
│ │ │ │ │
│ │ │ + │ ───→ 最终颜色 │
│ │ │ │ (眼睛看到的) │
│ │ └──────────┘ │
│ │ ▲ │
│ │ │ │
│ └──── × (1-α) ──┘ │
│ ▲ │
│ 背景色 (下层的颜色) │
│ │
└─────────────────────────────────────────────────┘
九、核心要点速记
| 要点 | 内容 |
|---|---|
| 公式 | 最终颜色 = 前景色 × α + 背景色 × (1 - α) |
| α 范围 | 0 (全透明) ~ 1 (不透明) |
| 算几次 | 每个 RGB 通道各算一次,共 3 次 |
| 依赖性 | 半透明色的最终呈现 必然依赖 背景色 |
| 百搭色 | 黑色半透明压暗、白色半透明提亮,都不会偏色 |
| 叠加 | 多层半透明是递归混合,不宜过多层 |
掌握了 Alpha 混合公式,你就能预测任何半透明色在任何背景上的表现,也能理解为什么设计稿搬到页面总会"翻车"——因为颜色从来不是孤立的,它永远是一场和背景的合谋。
如果喜欢这篇文章,欢迎点赞、转发🎨