Укрощаем режимы наложения в CSS
Недавно пришла в голову идея воссоздать такой эффект выборочного обесцвечивания на CSS:
С художественной точки зрения такой эффект уже давно заезжен, но мне была интересна именно техническая сторона вопроса. То есть задача — обесцветить всю картинку, при этом оставив нетронутым красный цвет.
Вот что получилось в результате экспериментов:
Давайте теперь разберёмся, как это работает.
Сразу стало понятно, что для решения нужно изготовить под конкретную картинку SVG-маску, затем наложить поверх цветной картинки чёрно-белую с вырезанной частью. В итоге, через дырку в чёрно-белом изображении будет просвечивать цветная. Но такое решение, с изготовлением маски вручную, неуниверсальное — под каждую картинку нужно отдельно делать SVG-маску, что довольно запарно и вообще не тру.
В поисках более универсального способа добиться нужного эффекта я решил попробовать сделать «маску на лету» из самого же оригинального изображения с помощью режимов наложения (blend modes).
Если вы не слышали раньше об этой фиче, то вкратце опишу её. По умолчанию слои в CSS располагаются друг поверх друга и не «просвечивают»:
А можно сделать так, чтобы слои не просто показывались один над другим, а «смешивались» по определённому алгоритму:
Режимы наложения пришли в CSS из Фотошопа благодаря сотрудникам компании Adobe. Но не все фотошоповские режимы наложения переехали в CSS — всего в нашем распоряжении есть 16 вариантов смешивания слоёв. Не буду вдаваться в подробности, с режимами наложения можно поиграть в демке Сары Суэйдан.
Но вернёмся к селективному обесцвечиванию.
Сначала наложим сверху на изображение его же копию с помощью псевдоэлемента:
.photo {
--source: url(http://source.unsplash.com/2uV2gMUEY9M); /* это нижний слой */
background-image: var(--source);
}.photo::after {
/* это верхний слой */
background-image: var(--source);
}
Дальше изменим у верхнего слоя режим наложения на lighten
. При таком режиме тёмные участки начинают «просвечивать», а светлые — наоборот остаются непрозрачными:
Режим наложения между слоями включается свойством mix-blend-mode
.
.photo::after {
mix-blend-mode: lighten;
}
Получилось то, что нужно: тёмный фон верхнего слоя стал прозрачным, а светлые участки — нет:
Далее обесцветим верхний слой с помощью CSS-фильтра:
.photo::after {
filter: grayscale(1);
}
Теперь верхний слой накладывается светлыми обесцвеченными участками на нижнюю цветную фотографию:
Уже почти то, что нужно. Надо теперь сделать светлые участки верхнего слоя более непрозрачными.
Для этого снова воспользуемся режимы наложения. Помимо свойства mix-blend-mode
, «смешивающее» два разных слоя, есть ещё одно свойство, меняющее режим наложения — это background-blend-mode
. Оно задаёт режим смешивания фоновых элементов одного слоя: изображения, фонового цвета, градиента.
Итак, зададим верхнему слою серый фоновый цвет и режим смешивания картинки с ним hard-light
. Это комбинированный режим, он чем-то похож на режим умножения — если коротко, он делает цвета ярче. Применительно в чёрно-белому изображению, этот режим сделает его светлые части более контрастными.
В итоге получим желаемый результат:
Остаётся разобраться, какие именно цвета в таком сочетании режимов наложения делаются прозрачными, а какие нет.
Для этого я сделал демку со слоями с полосатыми линейными градиентами и вот что получилось:
То есть при рассмотренной в этой статье комбинации режимов наложения прозрачными становятся области красного, синего и фиолетового оттенков, а желто-оранжевые, зелёные и голубые оттенки остаются непрозрачными.
Довольно любопытная фича, которую можно где-то использовать в своих проектах.