Укрощаем режимы наложения в CSS

Виталий Зюзин
4 min readApr 3, 2018

Недавно пришла в голову идея воссоздать такой эффект выборочного обесцвечивания на 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. Это комбинированный режим, он чем-то похож на режим умножения — если коротко, он делает цвета ярче. Применительно в чёрно-белому изображению, этот режим сделает его светлые части более контрастными.

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

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

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

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

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

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

--

--