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

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

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

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


person rmv    schedule 28.05.2018    source источник
comment
Я не знаю ответа на этот вопрос, но то же самое происходит в фотошопе, и я годами воспринимал это как часть компьютерной теории цвета. Я посмотрю вокруг, чтобы увидеть, если я могу найти больше информации.   -  person YAHsaves    schedule 29.05.2018
comment
Как бы то ни было, то же самое происходит в реальной жизни со всем, что не на 100% прозрачно и освещено спереди. Больше света от переднего объекта попадает в ваши глаза, поэтому его цвет оказывает большее влияние на окончательный цвет, даже если оба объекта имеют 50% прозрачности.   -  person jpa    schedule 29.05.2018
comment
@YAHsaves: среднее значение от 0 до 100 равно 50 (шаг 1). Среднее значение 50 и 150 равно 100 (шаг 2). Сравните это с: среднее значение 150 и 0 равно 75 (шаг 1). Среднее значение 75 и 100 составляет 87,5 (шаг 2). Проблема в том, что три числа не взвешиваются правильно. Среднее значение необходимо рассчитать на основе всех чисел одновременно; вы не можете просто рекурсивно вычислить среднее значение шаг за шагом. (Обратите внимание, что средний расчет по сути является расчетом прозрачности 50%. Расчет меняется для разных уровней прозрачности, но принцип остается тем же)   -  person Flater    schedule 29.05.2018
comment
Извлеченные уроки: с режимом «смешивание-смешивание: умножение» я получу цвет, не зависящий от порядка наложения — это то, что я изначально искал! Я думаю, что ответ @Moffens наиболее полезен для любого другого пользователя, сталкивающегося с той же проблемой. Но пояснения Темани Афифа на самом деле относятся к моему вопросу и описывают, почему HTML ведет себя иначе (имитирует физическое распространение света через полупрозрачную фольгу), поэтому зеленая галочка достается ему.   -  person rmv    schedule 29.05.2018


Ответы (4)


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

В первом случае вы видите 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% для второго:

.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%; таким образом, это непрозрачный цвет.


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

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
person Temani Afif    schedule 28.05.2018
comment
@ChrisHappy я не решаю проблему ;) я объясняю .. объяснение иногда лучше, чем исправление - person Temani Afif; 29.05.2018
comment
Проблема все еще возникает, даже если вы вынимаете div друг из друга и позиционируете: абсолютный их стек. - person YAHsaves; 29.05.2018
comment
@YAHsaves проблема возникнет, даже если мы используем несколько фонов или любую вещь, которая делает два цвета выше друг друга (используя абсолютное положение или нет);) - person Temani Afif; 29.05.2018
comment
Если вы хотите добавить TLDR, они будут мультипликативными, а не аддитивными. - person Caramiriel; 29.05.2018
comment
@Caramiriel: мультипликатив все равно не объясняет, почему операция не является коммутативной. - person Eric Duminil; 30.05.2018

Вы можете использовать свойство 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>

person Moffen    schedule 28.05.2018
comment
Практически любой режим смешивания цветов из этого list, который является коммутативным, добьется цели. - person Salman A; 29.05.2018
comment
Интересно, что это работает с Chrome, но не с Edge (по состоянию на последнее обновление Windows 10). - person Hong Ooi; 29.05.2018
comment
@Salman A: значит, поведение по умолчанию в HTML-CSS - это режим наложения = наложение, а не умножение, как я ошибочно ожидал? - person rmv; 29.05.2018
comment
@rmv почему вы ожидаете чего-то другого, кроме наложения / нормального? Каков ожидаемый цвет, если поместить непрозрачную синюю рамку перед красной рамкой? Это будет синий вместо красного или функция синего и красного. - person Salman A; 29.05.2018
comment
@SalmanA Мое первое наивное предположение заключалось в том, что оно должно быть коммутативным (например, аддитивное смешивание цветов), но, конечно, это зависит от порядка. На самом деле он имитирует распространение физического света, как указано в комментарии jpa. - person rmv; 29.05.2018
comment
@Moffen Отличный ответ, но не могли бы вы добавить объяснение, почему это работает и почему исходный код не работал? - person Bergi; 29.05.2018
comment
@rmv: То, что наиболее точно имитирует обычный режим смешивания, на самом деле смешивает краски. Например, если вы возьмете немного белой краски, замените половину ее красной краской, а затем замените половину получившейся розовой краски синей краской, вы получите голубовато-фиолетовый цвет (25% белого, 25% красного, 50% синий). Если вы начнете с той же белой краски, но сначала замените половину ее синей, а затем половину результата красной, вы получите красновато фиолетовый (25% белого, 25% синего, 50% красный) вместо этого. - person Ilmari Karonen; 29.05.2018

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

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

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

$(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: альфа-значение цвета переднего плана
Cb: цветовое значение цвета фона
αb: альфа-значение цвета фона
B: функция смешивания

person Salman A    schedule 29.05.2018

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

person Mr Lister    schedule 29.05.2018