Режимы наложения и их фоллбеки

Не так давно в Firefox 32 включили поддержку mix-blend-mode, a в Chrome 37CSS shapes, то есть возможность управлять формой, по которой текст будет обтекать элемент. Обе технологии выглядят очень интересно, так что я решила попробовать их в действии, заодно выяснив как будет выглядеть страница в браузерах, где они не поддерживаются.

В процессе написания статьи Safari обновился до версии 7.1, и в нем тоже появилась поддержка режимов наложения. Таким образом, режим наложения слоев фона имеет уже довольно неплохую поддержку браузерами, с наложением друг на друга элементов страницы дела пока обстоят похуже, но я уверена, что у них всё впереди. Также в Safari 7.1 стали доступны CSS shapes, хотя и с префиксом -webkit-.

По обеим темам уже были хорошие статьи на английском языке, например:

Для более глубокого изучения есть спецификации:

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

Для начала я открыла новый Хром и сделала такое:

See the Pen KCibG by yoksel (@yoksel) on CodePen.

Посмотреть в полный рост.

В демо используются mix-blend-mode и shape-outside. Режим наложения mix-blend-mode: multiply; задан всему контейнеру с текстом и картинками, а shape-outside для каждой картинки свой.

Полноценно оно отображается только в Safari 7.1, он умеет и режимы смешивания, и CSS shapes, но формы пока работают с префиксом -webkit-.

Также оно будет работать в последних Хроме/Опере со включенными экспериментальными возможностями (они умеют CSS shapes, но mix-blend-mode пока что за флагом, и их надо включать отдельно).

Включить в Хроме: chrome://flags/#enable-experimental-web-platform-features

В Опере: opera://flags/#enable-experimental-web-platform-features

В Safari и Chrome/Opera с флагами демо выглядит вот так:

Чтобы увидеть неработающие формы, достаточно открыть демо в Firefox:

а неработающие режимы смешивания — Internet Explorer:

Если нужно, чтобы формы работали везде, можно воспользоваться полифилом: css-shapes-polyfill, но следует иметь в виду, что это может создать дополнительную нагрузку для браузера. При этом в браузерах без поддержки CSS-форм ничего особо не ломается — картинки просто обтекаются текстом не по кривой, а по прямоугольному контуру.

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

.l-wrapper {
  ...
  padding: 40px 70px; /* добавляем отступы */
  background: #FFF;  /* добавляем фон */
  ...
  }

Очень хотелось воспользоваться @supports, чтобы убрать отступы в браузерах с поддержкой режимов наложения, но @supports не работает в Сафари.

Тогда мне пришло в голову, что можно сделать белые поля не создавая отступы, например, так:

.l-wrapper {
  ...
  background: #FFF;  /* добавляем фон */
  box-shadow: 0 0 0 60px #FFF /* добавляем поля */
  ...
  }

Результат: codepen.io/yoksel/full/AJKEh/

В браузерах с поддержкой режимов наложения ничего не изменилось, а в IE появился белый фон:

В продвинутых браузерах фона под текстом не будет, потому что при режиме смешивания multiply белый цвет исчезает.

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

В SVG-фильтрах есть режимы смешивания. Их меньше, чем в CSS, но среди них есть нужный нам multiply.

Создаем фильтр:

<svg class="svg-defs">
    <filter id="multiply" x="0" y="0">
        <!-- задаем картинку фона для фильтра -->
        <feImage id="bgimage" result="bgimage" x="0" y="0" width="300" height="206" xlink:href="http://img-fotki.yandex.ru/get/6846/5091629.a1/0_8558d_406830d_M"></feImage>
        <!-- задаем повторение фона -->
        <feTile in="bgimage"></feTile>
        <!-- накладываем картинку, к которой применен фильтр,
        на предыдущий фон с режимом multiply -->
        <feBlend mode="multiply" in2="SourceGraphic"/>
    </filter>
</svg>

Было бы куда удобнее задавать режим наложения исходной картинки на нижележащие слои страницы, а не на картинку, заданную в фильтре (здесь это feImage). Для этого в SVG предусмотрена возможность в качестве одного из источников фильтра задавать BackgroundImage — по смыслу это снимок экрана под областью действия фильтра. BackgroundImage позволил бы сделать фильтр гораздо короче:

<svg class="svg-defs">
    <filter id="multiply" x="0" y="0">
        <feBlend mode="multiply" in2="BackgroundImage" in="SourceGraphic"/>
    </filter>
</svg>

К сожалению, на момент написания статьи BackgroundImage в фильтрах не работает.

Чтобы фильтры работали везде, картинки, к которым они применяются, тоже надо завернуть в SVG. Примерный код:

<svg class="pic pic--dragon" viewBox="0 0 300 161">
    <image xlink:href="http://img-fotki.yandex.ru/get/6840/5091629.a1/0_855a9_b872dbe5_M" width="100%" height="100%"
        filter="url(#multiply)"/>
</svg>

Для SVG-элементов обязательно надо задавать размеры, можно с помощью CSS.

В тенях заголовка остался белый цвет. В случае с относительно однородным фоном (как в этом примере) можно заменить белый на более подходящий оттенок, с пестрым это не сработает, и придется придумывать что-то ещё.

Результат:

See the Pen Blend and shapes by yoksel (@yoksel) on CodePen.

SVG-фильтры для SVG элементов работают во всех современных браузерах, в IE начиная с 10-й версии. Фильтры были бы отличным решением, если бы не некоторые нюансы:

  1. Некоторые браузеры вообще не понимают SVG (IE8 и старше, например). Для них SVG-элементам нужно будет задать фоном растровые изображения.
  2. SVG-фильтры не работают в IE9. Его нужно будет как-то определить, чтобы задавать белую подложку для него и для более старых версий IE.
  3. При использовании фильтров фон под картинкой ресайзится вместе с картинкой. То есть картинка приклеивается к фону с применением заданного режима наложения, и дальнейшие манипуляции делаются уже с этой новой склейкой.
  4. При значительном изменении размеров картинки с фильтром между “плитками” фона могут появляться однопиксельные полосы.

Пример для проблем №3 и №4 (порастягивайте окно браузера):

See the Pen Resizing image with SVG-filter by yoksel (@yoksel) on CodePen.

На фонах с четким рисунком несовпадение и “размыливание” фона будет очень заметно.

Если исходное изображение имеет белые поля, можно попробовать использовать SVG-маску. Для этого делается копия картинки, где белые поля сделаны прозрачными или залиты черным, а часть картинки, которая должна быть показана — полностью белая. Картинка для рыцаря выглядит вот так:

Код маски:

<mask id="mask">
     <image xlink:href="http://img-fotki.yandex.ru/get/4509/5091629.a2/0_85a64_94d6d625_M" width="203" height="300"/>
  </mask>

Результат применения:

See the Pen Resizing image with SVG-filter by yoksel (@yoksel) on CodePen.

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

Способ с маской и фильтрами позволяет получить режимы смешивания в IE10+ и может сработать, если нужный режим наложения есть в SVG. Правда, этот способ отличают довольно громоздкие конструкции. Я бы предпочла вариант на чистом CSS, но не исключено, что способ с SVG окажется в каких-то ситуациях более подходящим.

Ссылки по теме:
Compositing and Blending Level 1
background-blend-mode
mix-blend-mode
CSS Shapes Module Level 1
CSS Shapes
SVG Filter Effects
SVG filters
Background-blend-mode
CSS и SVG маски
Грабли на чистом SVG
Если вы нашли ошибку или неточность, вы можете отредактировать статью с помощью prose.io, а также можно написать мне в комментариях или в Twitter.
Система комментирования от Disqus