CSS 半透明世界探险

Avatar of Ana Tudor
Ana Tudor

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 $200 免费积分!

最近,我被要求对一个登录页面进行一些调整,在代码中我发现有两个半透明叠加层——两者都使用相同的 RGB 值作为 background-color——叠加在图像之上。类似这样

<img src='myImage.jpg'/>
<div class='over1'></div>
<div class='over2'></div>

使用两个叠加层没有其他目的,只是因为只有一个叠加层无法对图像进行足够的着色。出于某种原因,最初编写该页面的开发人员认为,添加另一个半透明叠加层比增加第一个叠加层的 opacity 更好。

因此,我决定放弃一层,并给剩余的那层一个 opacity 值,使其在视觉效果上等同于初始的两个层叠加的结果。好吧,但是我们如何获得等同于一层 opacity 值呢?

如果您还记得我的 关于 mask 合成的速成课程,那么您可能已经猜到了答案,因为它是我们用于 add 合成操作的完全相同的公式!如果两个层的 alpha 值分别为 a0a1,则最终的 alpha 值为

a0 + a1 - a0⋅a1

下面的交互式演示显示了两个叠加层(您可以通过范围输入控制其 alpha 值 a0a1)与一个 alpha 值为 a0 + a1 - a0⋅a1 的叠加层的对比效果。

查看 thebabydino 在 CodePen 上的 作品

有趣的是,如果我们删除图像(通过演示底部的复选框),它们看起来完全相同,但如果图像在下方,它们看起来有点不同。也许这种差异只是我的眼睛在玩弄我,因为图像在某些部分更亮,而在其他部分更暗。

如果我们不将它们并排放置,而是只在 alpha 值为 a0a1 的两个层和 alpha 值为 a0 + a1 - a0⋅a1 的一层之间切换,则它们绝对看起来没有不同。

查看 thebabydino 在 CodePen 上的 作品

这可以扩展到多个层。在这种情况下,我们计算最下面两层的等效层,然后计算此结果层和它上面层的等效层,依此类推

Diagram. Illustrates how a bunch of semitransparent layers of various alphas are reduced to a single one. We start by taking the first two from the bottom and computing their equivalent, then we take this result and the third layer from the bottom and combine them into a single layer and so on.
将多个半透明层缩减为一个。

玩弄这个过程也让我想知道一个实心层(c0)与一个半透明叠加层(c1,alpha 值为 a)的实心 background 等效值。在这种情况下,一层等效值的 background 是基于每个通道计算的,结果通道为

ch0 + (ch1 - ch0)*a

…其中 ch0 是实心底层的通道(redgreenblue),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 上的 作品

根据设备、操作系统和浏览器,您可能会看到上面的演示中两个面板具有相同的背景……也可能没有。公式是正确的,但不同浏览器在不同操作系统和设备上处理两层情况的方式可能有所不同。

Screenshot collage.
预期结果是左侧面板相同,与我们有时在两层场景(右上角)和一层场景(右下角)之间获得的略微不同的结果相比。

我在 Twitter 上要求对一个简化的 测试用例 进行截图,从我收到的回复来看,两个面板在移动浏览器上始终看起来相同,无论我们是在谈论 Android 或 iOS 设备,还是 Firefox,无论操作系统如何。它们在 Windows 上也几乎总是相同的,虽然我确实收到了一条回复,让我知道 Chrome 和 Chromium Edge 有时可能会显示不同的两个面板。

当谈到 macOS 和 Linux 上的 WebKit 浏览器时,结果非常混杂,在大多数情况下,两个面板略有不同。也就是说,切换到 sRGB 配置文件 可以使它们相同。这里最有趣的事情是,在使用双显示器设置时,将窗口从一个显示器拖动到另一个显示器可以决定两个面板是否出现或消失。

但是,在实际使用场景中,这种差异很小,我们永远不会将两个面板并排放置。即使存在差异,没有人会知道,除非他们在不同的场景中测试页面,而这很可能是只有 Web 开发人员才会做的事情。而且,就像我们在不同的设备、操作系统和浏览器上查看相同普通实心背景的方式存在差异一样,我们也存在这种差异。例如,#ffa800 在我的 Ubuntu 和 Windows 笔记本电脑上看起来不一样,它在 CSS-Tricks 上被大量使用。同样,人们的眼中可能对事物的感知方式也不同。