堆叠的半透明盒子的颜色取决于订单?

Color of stacked semi-transparent boxes depends on order?

为什么两个堆叠的半透明盒子的最终颜色取决于顺序?

我怎样才能在两种情况下得到相同的颜色?

.a {
  background-color: rgba(255, 0, 0, 0.5)
}

.b {
  background-color: rgba(0, 0, 255, 0.5)
}
<span class="a"><span class="b">          Color 1</span></span>
<span class="b"><span class="a">Different Color 2</span></span>

仅仅是因为在这两种情况下,由于 top 层的不透明度如何影响 bottom[=99] 的颜色,颜色组合并不相同=]层.

对于第一种情况,您会在顶层看到 50% 的蓝色 和 50% 的透明。通过透明部分,你可以看到底层有 50% 的红色(所以我们总共只看到 25% 的红色)。第二种情况的逻辑相同(50% 的红色25% 的蓝色);因此你会看到不同的颜色,因为对于这两种情况,我们没有相同的比例。

为避免这种情况,您需要为两种颜色设置相同的比例。

这里有一个例子可以更好地说明和展示我们如何获得相同的颜色:

在顶层(内层)我们有 0.25 的不透明度(所以我们有 25% 的第一种颜色和 75% 的透明)然后对于底层(外层)我们具有 0.333 不透明度(所以我们有 75% 的 1/3 = 25% 的颜色,其余的是透明的)。我们在两层中的比例相同 (25%),所以即使我们颠倒层的顺序,我们也会看到 相同的颜色

.a {
  background-color: rgba(255, 0, 0, 0.333)
}

.b {
  background-color: rgba(0, 0, 255, 0.333)
}

.a > .b {
  background-color: rgba(0, 0, 255, 0.25)
}
.b > .a {
  background-color: rgba(255, 0, 0, 0.25)
}
<span class="a"><span class="b">          Color 1</span></span>
<span class="b"><span class="a">Different Color 2</span></span>

附带说明一下,白色背景也会影响颜色的渲染。它的比例是50%,这将使逻辑结果为100% (25% + 25% + 50%)。

您可能还会注意到,如果顶层的不透明度大于 0.5,则我们的两种颜色不可能具有相同的比例,因为 第一个将具有超过 50%,第二个将保持在 50% 以下:

.a {
  background-color: rgba(255, 0, 0, 1) /*taking 40% even with opacity:1*/
}

.b {
  background-color: rgba(0, 0, 255, 1) /*taking 40% even with opacity:1*/
}

.a > .b {
  background-color: rgba(0, 0, 255, 0.6) /* taking 60%*/
}
.b > .a {
  background-color: rgba(255, 0, 0, 0.6) /* taking 60%*/
}
<span class="a"><span class="b">          Color 1</span></span>
<span class="b"><span class="a">Different Color 2</span></span>

常见的琐碎情况是当顶层有 opacity:1 时,顶层颜色的比例为 100%;因此它是一种不透明颜色。


为了更准确和精确的解释,这里是用于计算我们在两层组合后看到的颜色的公式ref:

ColorF = (ColorT*opacityT + ColorB*OpacityB*(1 - OpacityT)) / factor

ColorF 是我们最终的颜色。 ColorT/ColorB分别是顶部和底部的颜色。 opacityT/opacityB分别为每种颜色定义的顶部和底部不透明度:

factor 由这个公式定义 OpacityT + OpacityB*(1 - OpacityT).

很明显,如果我们切换两层,factor 不会改变(它将保持不变)但是我们可以清楚地看到每种颜色的比例都会改变,因为我们没有相同的乘数。

对于我们的初始情况,两个不透明度都是 0.5 所以我们将有:

ColorF = (ColorT*0.5 + ColorB*0.5*(1 - 0.5)) / factor

如上所述,顶部颜色的比例为 50% (0.5),底部颜色的比例为 25% (0.5*(1-0.5)),因此切换图层也会切换这些比例;因此我们看到了不同的 final 颜色。

现在,如果我们考虑第二个例子,我们将得到:

ColorF = (ColorT*0.25 + ColorB*0.333*(1 - 0.25)) / factor

在这种情况下,我们有 0.25 = 0.333*(1 - 0.25),因此切换两层不会有任何效果;因此颜色将保持不变。

我们也可以清楚地识别出这些琐碎的案例:

  • 当顶层有opacity:0时公式等于ColorF = ColorB
  • 当顶层有opacity:1时公式等于ColorF = ColorT

您可以使用 css 属性, mix-blend-mode : multiply (limited browser support)

.a {
  background-color: rgba(255, 0, 0, 0.5);
  mix-blend-mode: multiply;
}

.b {
  background-color: rgba(0, 0, 255, 0.5);
  mix-blend-mode: multiply;
}

.c {
  position: relative;
  left: 0px;
  width: 50px;
  height: 50px;
}

.d {
  position: relative;
  left: 25px;
  top: -50px;
  width: 50px;
  height: 50px;
}
<span class="a"><span class="b">          Color 1</span></span>
<span class="b"><span class="a">Different Color 2</span></span>

<div class="c a"></div>
<div class="d b"></div>

<div class="c b"></div>
<div class="d a"></div>

有关发生的情况的解释,请参阅 Temani Afif 的回答。
作为替代解决方案,您可以采用一个跨度,例如 a,定位它并给它一个较低的 z-index,如果它在 b 内。然后堆叠将始终相同:b 在第一行中绘制在 a 之上,而 a 在第二行中绘制在 b 下方。

.a {
  background-color: rgba(255, 0, 0, 0.5);
}

.b {
  background-color: rgba(0, 0, 255, 0.5);
}

.b .a {position:relative; z-index:-1;}
<span class="a"><span class="b">     Color 1</span></span>
<span class="b"><span class="a">Same Color 2</span></span>

您正在按以下顺序混合三种颜色:

  • rgba(0, 0, 255, 0.5) over (rgba(255, 0, 0, 0.5) over rgba(255, 255, 255, 1))
  • rgba(255, 0, 0, 0.5) over (rgba(0, 0, 255, 0.5) over rgba(255, 255, 255, 1))

你会得到不同的结果。这是因为使用 normal blend mode1,2 which is not commutative3 将前景颜色与背景颜色混合。而且由于它不可交换,交换前景色和背景色会产生不同的结果。

1 混合模式是一个函数,它接受前景和背景颜色,应用一些公式和 returns 结果颜色。

2 给定两种颜色,背景和前景,普通混合模式只是 returns 前景色。

3 如果更改操作数的顺序不会改变结果,则操作是可交换的,例如加法是可交换的 (1 + 2 = 2 + 1) 而减法不是 (1 - 2 ≠ 2 - 1)。

解决方案是使用可交换的混合模式:returns 同一颜色以任何顺序对同一对颜色使用相同的颜色(例如乘法混合模式,它将两种颜色相乘,returns 生成的颜色;或加深混合模式,returns 两者中较暗的颜色)。

$(function() {
  $("#mode").on("change", function() {
    var mode = $(this).val();
    $("#demo").find(".a, .b").css({
      "mix-blend-mode": mode
    });
  });
});
#demo > div {
  width: 12em;
  height: 5em;
  margin: 1em 0;
}

#demo > div > div {
  width: 12em;
  height: 4em;
  position: relative;
  top: .5em;
  left: 4em;
}

.a {
  background-color: rgba(255, 0, 0, 0.5);
}

.b {
  background-color: rgba(0, 0, 255, 0.5);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<select id="mode">
  <optgroup label="commutative">
    <option>multiply</option>
    <option>screen</option>
    <option>darken</option>
    <option>lighten</option>
    <option>difference</option>
    <option>exclusion</option>
  </optgroup>
  <optgroup label="non-commutative">
    <option selected>normal</option>
    <option>overlay</option>
    <option>color-dodge</option>
    <option>color-burn</option>
    <option>hard-light</option>
    <option>soft-light</option>
    <option>hue</option>
    <option>saturation</option>
    <option>color</option>
    <option>luminosity</option>
  </optgroup>
</select>

<div id="demo">
  <div class="a">
    <div class="b"></div>
  </div>
  <div class="b">
    <div class="a"></div>
  </div>
</div>


为了完整起见,这里是计算合成颜色的公式:

αs x (1 - αb) x Cs + αs x αb x B(Cb, Cs) + (1 - αs) x αb x Cb

与:

Cs:前景色的颜色值
αs:前景色的alpha值
cb: 背景色的颜色值
αb: 背景色的alpha值
B: 混合函数