[Перевод] CSS: приключения в стране полупрозрачности
Недавно меня попросили подправить один лендинг. Среди того, что я нашла в его коде, было изображение, поверх которого были расположены два перекрывающих его полупрозрачных элемента. Оба — с одними и теми же RGB-значениями цвета в свойстве background-color
. Выглядело это примерно так:
Незачем было применять два таких элемента. Это могло понадобиться разве что из-за того, что только один такой элемент недостаточно сильно тонировал изображение. По какой-то причине автор страницы решил, что добавление ещё одного полупрозрачного элемента, накладывающегося на изображение, было лучше, чем увеличение значения параметра opacity
первого.
В результате я решила избавиться от одного из слоёв и задать параметр opacity
оставшегося так, чтобы внешне изображение выглядело так же, как и прежнее. Всё это хорошо, но тут возник вопрос: как настроить параметр opacity
оставшегося элемента так, чтобы результат был таким же, как при применении двух полупрозрачных слоёв?
Если вы читали эту мою статью о композиции масок, то вы уже, возможно, догадались о том, как подобрать подходящее значение. Ведь тут используется та же самая формула, которая применялась там для композиции полупрозрачных элементов с использованием mask-composite: add
. Если есть два слоя со значениями прозрачности, равными a0
и a1
, то значение результирующего показателя вычисляется так:
a0 + a1 - a0*a1
Здесь можно найти интерактивную демонстрацию, взглянув на которую можно сравнить то, как выглядит изображение, тонированное с использованием двух слоёв, свойства opacity
которых (a0
и a1
) можно контролировать с помощью ползунков, и такое же изображение, тонированное с помощью слоя, свойство opacity
которого рассчитывается по формуле a0 + a1 — a0*a1
.
Варианты тонирования изображения, две копии которого выводятся рядом друг с другом
Интересно то, что области, в которых расположены два полупрозрачных элемента, и один такой элемент, выглядят одинаково в том случае, если изображение под ними не выводится (отключить вывод изображения можно с помощью флажка, расположенного в нижней части экрана). А вот если под этими слоями выводится изображение, то два его варианта немного отличаются. Возможно эта разница — лишь результат оптического обмана, может быть, некоторые части изображений мне кажутся светлее или темнее, чем они есть на самом деле.
Они, определённо, не выглядят разными, если не выводить их рядом друг с другом, а просто переключаться между двумя слоями, значения свойств opacity
которых равняются a0
и a1
, и одним слоем, значение opacity
которого найдено по формуле a0 + a1 — a0*a1
. Вот соответствующий пример.
Варианты тонирования изображения с возможностью переключения между одним и двумя полупрозрачными слоями
Этот пример можно расширить и до большего количества слоёв. В таком случае сначала рассчитывают прозрачность нового слоя, эквивалентную прозрачности двух нижних слоёв. Затем находят прозрачность слоя, эквивалентную прозрачности нового слоя, и слоя, расположенного сразу над ним. Эта операция повторяется до тех пор, пока не будут обработаны все слои.
Сведение нескольких полупрозрачных слоёв к одному слою
Подобные эксперименты заставили меня задуматься о том, как рассчитать параметры непрозрачного фонового изображения, являющегося эквивалентом непрозрачного слоя (c0
) и наложенного на него полупрозрачного слоя (c1
, прозрачность которого установлена в значение a
). В данном случае итоговое изображение, представляющее собой один слой, можно найти, обрабатывая исходное изображения поканально, при этом значения цвета итоговых каналов вычисляются по такой формуле:
ch0 + (ch1 - ch0)*a
Здесь ch0
— это канал непрозрачного нижнего слоя (red
, green
или blue
— для красного, зелёного и синего цветов), ch1
— это соответствующий канал верхнего полупрозрачного слоя, а показатель a
— это показатель, задающий прозрачность того же самого полупрозрачного слоя.
Если выразить эти рассуждения в виде SCSS-кода, то получится следующее:
/* функция для работы с данными каналов */
@function res-ch($ch0, $ch1, $a) {
@return $ch0 + ($ch1 - $ch0)*$a
}
@function res-col($c0, $c1, $a) {
$ch: 'red' 'green' 'blue'; /* имена каналов */
$nc: length($ch); /* количество каналов */
$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)
}
Вот интерактивный пример. Поэкспериментировав с ним, можно сравнить то, как выглядят два слоя и их эквивалент, представленный в виде одного слоя. Здесь можно выбирать RGB-значения цвета двух слоёв, а также уровень прозрачности второго слоя.
Результаты наложения полупрозрачного слоя на непрозрачный слой
В зависимости от устройства, операционной системы и браузера, можно будет увидеть, что цветные панели в этом примере выглядят одинаково… или нет. Формула корректна, но то, как именно два исходных слоя обрабатываются в разных средах, может различаться.
Левая часть изображения — это ожидаемый результат сведения двух слоёв к одному. В правой части показано, как при сведении двух слоёв (из правой верхней части изображения) может получиться слой, показанный справа внизу, который выглядит не так, как ожидается
Я попросила пользователей Твиттера прислать мне скриншоты, сделанные с применением упрощённой тестовой версии этого примера. Если судить по полученным мной откликам, то возникает такое впечатление, что две панели всегда выглядят одинаково в мобильных браузерах (и в Android, и в iOS), а также — в браузере Firefox независимо от операционной системы. Практически всегда панели выглядят одинаково в Windows, хотя я получила и ответы, указывающие на то, что Chrome и Chromium Edge иногда отображают панели не так, как ожидается.
Если говорить о браузерах, основанных на WebKit, работающих в macOS и Linux, то можно отметить, что полученные результаты очень неоднородны. В большинстве случаев цвет панелей слегка отличается. Но использование цветового профиля sRGB помогает сделать так, чтобы панели выглядели бы одинаковыми. Самое интересное начинается в том случае, если этот пример рассматривать в системе, в которой используется два монитора. Перетаскивание окна с одного монитора на другой может привести к тому, что разница между панелями оказывается то видна, то не видна.
Надо отметить, что в реальных сценариях разница весьма невелика, и что пара подобных панелей вряд ли окажется рядом друг с другом. Но даже если разница и будет, никто об этом не узнает до тех пор, пока не протестирует страницу в разных окружениях. В любом случае, подобным, вероятно, может заняться лишь веб-разработчик. Кроме того, возникает такое ощущение, что цвета обычных непрозрачных элементов тоже могут выглядеть по-разному при использовании разных устройств, операционных систем и браузеров. Например, цвет #ffa800
, который широко используется на сайте css-tricks.com, выглядит по-разному на моих ноутбуках с Ubuntu и Windows. Но ведь и глаза разных людей могут по-разному воспринимать одни и те же цвета.
Уважаемые читатели! Сталкивались ли вы с проблемами, связанными с тем, что разные браузеры, операционные системы или устройства по-разному выводят цвета веб-страниц?