Укрощаем режимы наложения в 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. При таком режиме тёмные участки начинают «просвечивать», а светлые — наоборот остаются непрозрачными:

Режим наложения lighten

Режим наложения между слоями включается свойством mix-blend-mode .

.photo::after {
mix-blend-mode: lighten;
}

Получилось то, что нужно: тёмный фон верхнего слоя стал прозрачным, а светлые участки — нет:

Далее обесцветим верхний слой с помощью CSS-фильтра:

.photo::after {
filter: grayscale(1);
}

Теперь верхний слой накладывается светлыми обесцвеченными участками на нижнюю цветную фотографию:

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

Для этого снова воспользуемся режимы наложения. Помимо свойства mix-blend-mode, «смешивающее» два разных слоя, есть ещё одно свойство, меняющее режим наложения — это background-blend-mode. Оно задаёт режим смешивания фоновых элементов одного слоя: изображения, фонового цвета, градиента.

Итак, зададим верхнему слою серый фоновый цвет и режим смешивания картинки с ним hard-light. Это комбинированный режим, он чем-то похож на режим умножения — если коротко, он делает цвета ярче. Применительно в чёрно-белому изображению, этот режим сделает его светлые части более контрастными.

В итоге получим желаемый результат:


Остаётся разобраться, какие именно цвета в таком сочетании режимов наложения делаются прозрачными, а какие нет.

Для этого я сделал демку со слоями с полосатыми линейными градиентами и вот что получилось:

Вживую можно посмотреть в демке

То есть при рассмотренной в этой статье комбинации режимов наложения прозрачными становятся области красного, синего и фиолетового оттенков, а желто-оранжевые, зелёные и голубые оттенки остаются непрозрачными.

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

Виталий Зюзин

Written by

Автор курсов в HTML Academy.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade