Цвет складываемых полупрозрачных коробок зависит от заказа?

86

Почему окончательный цвет двух уложенных друг на друга полупрозрачных коробок зависит от порядка?

Как сделать так, чтобы в обоих случаях я получал одинаковый цвет?

.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>

RMV
источник
7
Я не знаю ответа на этот вопрос, но то же самое происходит в фотошопе, и это то, что я принимал годами как часть компьютерной теории цвета. Я посмотрю, смогу ли я найти дополнительную информацию.
YAHsaves
11
Как бы то ни было, то же самое происходит в реальной жизни со всем, что не на 100% прозрачно и освещено спереди. Больше света от переднего объекта попадает в ваш глаз, поэтому его цвет имеет большее влияние на окончательный цвет, даже если оба имеют прозрачность 50%.
jpa
4
@YAHsaves: среднее значение 0 и 100 равно 50 (шаг 1). Среднее значение 50 и 150 равно 100 (шаг 2). Сравните это с: среднее значение 150 и 0 равно 75 (шаг 1). Среднее значение 75 и 100 составляет 87,5 (шаг 2). Проблема в том, что три числа неправильно взвешиваются. Среднее значение необходимо рассчитывать на основе всех чисел одновременно; вы не можете просто рекурсивно вычислить среднее значение шаг за шагом. (Обратите внимание, что средний расчет - это, по сути, расчет 50% прозрачности. Расчет меняется для разных уровней прозрачности, но принцип остается тем же самым)
Flater
2
Извлеченные уроки: с помощью 'mix-blend-mode: multiply' я получу цвет независимо от порядка наложения - это то, что я изначально искал! Я думаю, что ответ @Moffens наиболее полезен для любого другого пользователя, столкнувшегося с той же проблемой. Но объяснения Темани Афифа действительно применимы к моему вопросу и описывают, почему HTML ведет себя иначе (имитирует физическое распространение света через полупрозрачную фольгу), поэтому зеленая галочка идет ему.
RMV

Ответы:

103

Просто потому, что в обоих случаях комбинация цветов не одинакова из-за того, как непрозрачность верхнего слоя влияет на цвет нижнего слоя.

В первом случае вы видите 50% синего и 50% прозрачного в верхнем слое. Через прозрачную часть вы видите 50% красного цвета в нижнем слое (так что всего мы видим только 25% красного ). Та же логика для второго случая ( 50% красного и 25% синего ); Таким образом, вы увидите разные цвета, потому что в обоих случаях у нас разные пропорции.

Чтобы этого избежать, вам нужно иметь одинаковую пропорцию для обоих цветов.

Вот пример, чтобы лучше проиллюстрировать и показать, как мы можем получить тот же цвет:

В верхнем слое (внутренний диапазон) у нас есть 0.25непрозрачность (так что у нас есть 25% первого цвета и 75% прозрачности), затем для нижнего слоя (внешний диапазон) у нас есть 0.333непрозрачность (так что у нас есть 1/3 75% = 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%. один:

Обычный тривиальный случай - это когда верхний слой имеет opacity:1100% цвет верхней части; таким образом, это непрозрачный цвет.


Для более точного и точного объяснения, вот формула, используемая для расчета цвета, который мы видим после комбинации обоих слоев 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)), поэтому переключение слоев также изменит эти пропорции; таким образом, мы видим другой конечный цвет.

Теперь, если мы рассмотрим второй пример, у нас будет:

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
Темани Афиф
источник
23
@ChrisHappy я не решаю проблему;) я объясняю .. иногда объяснение лучше, чем исправление
Темани Афиф
Проблема все еще возникает, даже если вы извлекаете div друг из друга и позиционируете их для стека.
YAHsaves
1
@YAHsaves, проблема будет, даже если мы используем несколько фонов или что-то, что делает два цвета друг над другом (используя абсолютное положение или нет);)
Темани Афиф
5
Если вы хотите добавить TL; DR, возможно, они будут мультипликативными, а не аддитивными.
Карамириэль
2
@Caramiriel: «мультипликативный» все равно не объясняет, почему операция не коммутативна.
Эрик Думинил
41

Вы можете использовать свойство css mix-blend-mode : multiply (ограниченная поддержка браузера )

.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>

Моффен
источник
6
Практически любой режим смешивания цветов из этого списка, который является «коммутативным», подойдет.
Salman A
Интересно, что это работает с Chrome, но не с Edge (в последнем обновлении Windows 10).
Hong Ooi
3
@rmv, почему вы ожидаете чего-либо, кроме оверлея / нормального? Какого цвета вы ожидаете, если расположите непрозрачный синий прямоугольник перед красным? Он будет синим вместо красного или функция синего и красного.
Salman A
1
@Moffen Отличный ответ, но не могли бы вы добавить объяснение, почему это работает, и почему исходный код не работает?
Берги
1
@rmv: «Обычный» режим смешивания больше всего имитирует смешивание красок. Например, если вы возьмете немного белой краски, замените ее половину красной краской, а затем замените половину полученной розовой краски синей краской, вы получите синевато-фиолетовый (25% белого, 25% красного, 50% синий). Если вы начнете с той же белой краски, но сначала замените ее половину на синюю, а затем половину результата на красную, вы получите красновато- пурпурный (25% белого, 25% синего, 50% красного).
Илмари Каронен
22

Вы смешиваете три цвета в следующем порядке:

  • 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))

И получаешь разные результаты. Это связано с тем, что цвет переднего плана смешивается с цветом фона с использованием обычного режима наложения 1,2, который не является коммутативным 3 . А поскольку он не коммутативен, замена цвета переднего плана и фона даст другой результат.

1 Режим наложения - это функция, которая принимает цвет переднего плана и фона, применяет некоторую формулу и возвращает результирующий цвет.

2 При наличии двух цветов, фона и переднего плана, обычный режим наложения просто возвращает цвет переднего плана.

3 Операция является коммутативной, если изменение порядка операндов не меняет результат, например, сложение коммутативно (1 + 2 = 2 + 1), а вычитание - нет (1-2 ≠ 2 - 1).

Решение состоит в том, чтобы использовать режим наложения, который является коммутативным: тот, который возвращает один и тот же цвет для той же пары цветов в любом порядке (например, режим наложения умножения, который умножает оба цвета и возвращает результирующий цвет; или режим наложения затемнения, который возвращает темный цвет из двух).


Для полноты картины приведем формулу для расчета комбинированного цвета:

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

с участием:

Cs: значение цвета переднего плана;
s: значение альфа цвета переднего плана;
Cb: значение цвета фона;
αb: значение альфа цвета фона;
B: функция смешивания.

Салман А
источник
8

Для объяснения того, что происходит, см. Ответ Темани Афиф.
В качестве альтернативного решения вы можете взять один промежуток, 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>

Г-н Листер
источник