最近,我被要求对一个登录页面进行一些调整,在代码中我发现有两个半透明叠加层——两者都使用相同的 RGB 值作为 background-color
——叠加在图像之上。类似这样
<img src='myImage.jpg'/>
<div class='over1'></div>
<div class='over2'></div>
使用两个叠加层没有其他目的,只是因为只有一个叠加层无法对图像进行足够的着色。出于某种原因,最初编写该页面的开发人员认为,添加另一个半透明叠加层比增加第一个叠加层的 opacity
更好。
因此,我决定放弃一层,并给剩余的那层一个 opacity
值,使其在视觉效果上等同于初始的两个层叠加的结果。好吧,但是我们如何获得等同于一层 opacity
值呢?
如果您还记得我的 关于 mask
合成的速成课程,那么您可能已经猜到了答案,因为它是我们用于 add
合成操作的完全相同的公式!如果两个层的 alpha 值分别为 a0
和 a1
,则最终的 alpha 值为
a0 + a1 - a0⋅a1
下面的交互式演示显示了两个叠加层(您可以通过范围输入控制其 alpha 值 a0
和 a1
)与一个 alpha 值为 a0 + a1 - a0⋅a1
的叠加层的对比效果。
查看 thebabydino 在 CodePen 上的 作品。
有趣的是,如果我们删除图像(通过演示底部的复选框),它们看起来完全相同,但如果图像在下方,它们看起来有点不同。也许这种差异只是我的眼睛在玩弄我,因为图像在某些部分更亮,而在其他部分更暗。
如果我们不将它们并排放置,而是只在 alpha 值为 a0
和 a1
的两个层和 alpha 值为 a0 + a1 - a0⋅a1
的一层之间切换,则它们绝对看起来没有不同。
查看 thebabydino 在 CodePen 上的 作品。
这可以扩展到多个层。在这种情况下,我们计算最下面两层的等效层,然后计算此结果层和它上面层的等效层,依此类推

玩弄这个过程也让我想知道一个实心层(c0
)与一个半透明叠加层(c1
,alpha 值为 a
)的实心 background
等效值。在这种情况下,一层等效值的 background
是基于每个通道计算的,结果通道为
ch0 + (ch1 - ch0)*a
…其中 ch0
是实心底层的通道(red
、green
或 blue
),ch1
是顶层半透明层的相应通道,a
是同一个顶层半透明层的 alpha 值。
将其放入 Sass 代码中,我们得到
/* per channel function */
@function res-ch($ch0, $ch1, $a) {
@return $ch0 + ($ch1 - $ch0)*$a
}
@function res-col($c0, $c1, $a) {
$ch: 'red' 'green' 'blue'; /* channel names */
$nc: length($ch); /* number of channels */
$ch-list: ();
@for $i from 0 to $nc {
$fn: nth($ch, $i + 1);
$ch-list: $ch-list,
res-ch(call($fn, $c0), call($fn, $c1), $a);
}
@return RGB($ch-list)
}
下面的交互式演示(它允许我们通过单击色板和 alpha 值分别选择两个层的 RGB 值和顶层层的 alpha 值)显示了两个层与我们计算的一层等效层的对比效果。
查看 thebabydino 在 CodePen 上的 作品。
根据设备、操作系统和浏览器,您可能会看到上面的演示中两个面板具有相同的背景……也可能没有。公式是正确的,但不同浏览器在不同操作系统和设备上处理两层情况的方式可能有所不同。

我在 Twitter 上要求对一个简化的 测试用例 进行截图,从我收到的回复来看,两个面板在移动浏览器上始终看起来相同,无论我们是在谈论 Android 或 iOS 设备,还是 Firefox,无论操作系统如何。它们在 Windows 上也几乎总是相同的,虽然我确实收到了一条回复,让我知道 Chrome 和 Chromium Edge 有时可能会显示不同的两个面板。
当谈到 macOS 和 Linux 上的 WebKit 浏览器时,结果非常混杂,在大多数情况下,两个面板略有不同。也就是说,切换到 sRGB 配置文件 可以使它们相同。这里最有趣的事情是,在使用双显示器设置时,将窗口从一个显示器拖动到另一个显示器可以决定两个面板是否出现或消失。
但是,在实际使用场景中,这种差异很小,我们永远不会将两个面板并排放置。即使存在差异,没有人会知道,除非他们在不同的场景中测试页面,而这很可能是只有 Web 开发人员才会做的事情。而且,就像我们在不同的设备、操作系统和浏览器上查看相同普通实心背景的方式存在差异一样,我们也存在这种差异。例如,#ffa800
在我的 Ubuntu 和 Windows 笔记本电脑上看起来不一样,它在 CSS-Tricks 上被大量使用。同样,人们的眼中可能对事物的感知方式也不同。
有趣。我最近也做了一些类似的观察。我不得不重建一个现有的动画,我并不知道原始的颜色和不透明度值。为了获得它们,我取了一些像素值,将它们代入混合公式,并创建了一个线性方程组,然后求解 RGB 值和不透明度。
考虑到动画是 3D 的,我为一些使用的坐标做了类似的事情,以确定尺寸和变换和投影矩阵。很有趣的东西。
使用混合时要考虑的一件事是伽马校正。我不确定 Web 标准或浏览器如何处理这一点,但不同的处理方式也可能导致非常明显的差异
https://en.wikipedia.org/wiki/Alpha_compositing#Composing_alpha_blending_with_gamma_correction
相关地,渐变在 RGB 中也存在伽马和插值问题。如果它们使用正确的插值创建,它们通常看起来会好得多,因为它是更平滑的过渡。如果您有兴趣,请查看此处渐变部分:https://blog.johnnovak.net/2016/09/21/what-every-coder-should-know-about-gamma/
这里还有一个很好的讨论:https://github.com/w3c/csswg-drafts/issues/4647
不过我跑题了……感谢您的文章!