Буквально недавно Крис написал про «Эффект капельного преобразования в CSS». Эффект реально крутой и сама техника реализована по-умному, но данный подход через обычные CSS фильтры имеет определенные недостатки: нельзя использовать непрозрачность, добавлять контент внутрь капель, проблемы с фоновыми цветами.
В последние дни я достаточно много экспериментировал с SVG фильтрами и заметил, что с их помощью можно решить вышеописанные проблемы в CSS реализации. Посмотрите на липкое меню, которое я сделал для демонстрации:
SVG Filters 101
SVG фильтры — это очень мощная штука. Но это очень обширная тема, поэтому мы поговорим только о самом необходимом для решения нашей задачи.
Несмотря на название мы можем применять эти фильтры к DOM элементам с помощью CSS и это будет работать в большинстве браузеров.
Классический синтекс для описания фильтров:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<filter id="name-your-filter-here">
...
<!-- insert filters here -->
...
</filter>
...
</defs>
</svg>
Применение фильтра к DOM элементу:
.selector {
filter: url('#name-of-your-filter-here');
/* you can also load filters from external SVGs this way: */
filter: url('filters.svg#name-of-your-other-filter-here');
}
Вам могут понадобиться вендорные префиксы для свойства filter.
Элемент содержит в себе от одного и более фильтровых приметив (filter primitives), которые выполняют функции blur, color transform, shading. Полные перечень этих примитив находится тут.
Взглянем на парочку примеров:
<filter id="blur">
<feGaussianBlur in="SourceGraphic" stdDeviation="3" />
</filter>
Этот простой фильтр выполняет трех-пиксельное размытие элемента. Важно обратить внимание на атрибут in=«SourceGraphic». В in определяется к чему именно будет применяться фильтр. Значение SourceGraphic возвращает оригинальный элемент. Таким образом мы указываем, что размытие должно происходить для оригинального графического объекта. Все достаточно просто.
Давайте рассмотрим пример немного посложнее: drop shadow. В нем наглядно показано как работает цепочка фильтровых приметив вместе:
<filter id="drop-shadow">
<feGaussianBlur in="SourceGraphic" stdDeviation="7" result="shadow" />
<feOffset in="shadow" dx="3" dy="4" result="shadow" />
<feColorMatrix in="shadow" mode="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.6 0" result="shadow" />
<feBlend in="SourceGraphic" in2="shadow" />
</filter>
Взгляните на result и in атрибуты. В result указывается имя результата с применением фильтра, что в последствии дает нам возможность фильтровать этот результат в отличии от оригинального графического объекта. Тоесть в этом примере мы произвели размытие для элемента, затемнили конкретно размытый объект, а потом уже изменили позиции для размытого и затемненного объектов.
Обратите внимание на приметиву <feBlend>. Она содержит два атрибута для обозначения области применения: in со значением SourceGraphic и in2 со значением shadow.
Теперь, когда мы понимает базовые принципы работы SVG фильтров, давайте разберемся как делать липкий эффект.
Закрепим уже пройденное
Основная техника описана здесь. Напомню, что идея заключается в том, чтобы одновременно размывать и контрастировать элементы. И все заработает магическим образом.
CodePen
Однако, мы по прежнему имеем:
- Проблемы в работе с разным цветами.
- Размытие производится для всего элемента, включая содержимое.
- Невозможность использовать непрозрачность.
Все это не позволяет применить данный трюк в реальном проекте.
С помощью SVG фильтров мы можем реализовать то, что не было возможно с CSS: мы можем увеличивать контрастность только для альфа-канала, не изменяя цвета; и с помощью SourceGraphic мы можем применять размытие только для самого элемента, не меняя содержимое.Также поскольку мы работаем с альфа-каналом, то не только он должен быть прозрачным, прозрачный фон необходим, будьте осторожны с этим.
Основной код:
<filter id="goo">
<feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur" />
<feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7" result="goo" />
<feBlend in="SourceGraphic" in2="goo" />
</filter>
Коротко о том, что мы сделали:
- Во-первых мы произвели размытие в 10 пикселей и присвоим имя данному результату.
- Затем для получившегося результата мы применили color matrix filter для увеличения контрастности альфа-канала.
- И после этого мы вставили оригинальным графический объект в этот эффект.
О Color Matrices
Если вы до этого не использовали color matrix filter, то тогда необходимо пояснить как это работает. Представьте таблицу из четырех строк и пяти столбцов. Она будет выглядеть следующим образом.
| R | G | B | A | +
---|-------------------
R | 1 | 0 | 0 | 0 | 0
---|-------------------
G | 0 | 1 | 0 | 0 | 0
---|-------------------
B | 0 | 0 | 1 | 0 | 0
---|-------------------
A | 0 | 0 | 0 | 1 | 0
---|-------------------
Каждая строка представляет из себя канал (red, green, blue и alpha) и использует для установки значения канала. Первые четыре столбца также представляют из себя каналы. Число в клетке являет мультипликатором канала в столбце для канала в строке. Например, 0.5 в строке R и столбце G добавит к красному каналу текущее значение Green*0.5. Последняя колонка уже не представляет из себя канал и используется для добавления или вычитания. Числа указанные в ней умножаются на 255 и присваиваются соответствующему каналу.
Это долго объяснять, но на самом деле использование фильтра предельно простое. В нашем случае мы изменяем только значение альфа-канала и наша матрица будет выглядеть так:
| R | G | B | A | +
---|-------------------
R | 1 | 0 | 0 | 0 | 0
---|-------------------
G | 0 | 1 | 0 | 0 | 0
---|-------------------
B | 0 | 0 | 1 | 0 | 0
---|-------------------
A | 0 | 0 | 0 |18 |-7
---|-------------------
RGB каналы остаются неизмененными. Значение альфа-канала умножается 18, а затем из него вычитается 7*255, эффективно увеличивая контраст одной только прозрачности. Все значения можно настроить под ваши потребности.
Чтобы применить эту матрицу для feColorMatrix фильтра, мы должны написать все значения в определенном порядке:
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7"
Демо
В итоге задуманный нами эффект работает. Пример:
Вы можете кастомизировать все, что вам нужно, добавить тень, изменить цвет каждого элемента, все в вашем распоряжении!
Подытожим
- Фильтр должен применять к контейнеру с элементами, а не к самим элементам.
- Из за самого тянущего эффекта контейнер должен быть немного большей области чем содержимое. В противном случае вы можете получить подобные дефекты на краях:
- Для того, чтобы применять данный фильтр к примеру для прямоугольников мы должны воспользоваться немного более изощренным способом. Вместо отрисовки оригинального объекта над липкоим эффектом, мы должны применить feComposite фильтр с атрибутом atop, для того чтобы скрыть все, что выходит за рамки:
<filter id="fancy-goo"> <feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur" /> <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 19 -9" result="goo" /> <feComposite in="SourceGraphic" in2="goo" operator="atop"/> </filter>
CodePen
Данный подход вы можете использовать не только для создания липкого эффекта, но и в более простых случаях, например, для закругления углов нескольких прямоугольников. - Данный фильтр хоть и легок в своем объеме, но может быть очень ресурсоемким, если применяется для больших площадей. Поэтому будьте осторожны.
Поддержка
SVG фильтры имеют хорошую поддержку, но не все браузеры поддерживают их применение к DOM элементам, в частности, Safari. Однако они действительно работают, по крайней мере, над Firefox и Хромом, даже версией Android. Но фильтр заметно портит картину если не поддерживается. В случае если его использование вам необходимо, то используйте его для элементов SVG вместо элементов DOM.
Автор: ilusha_sergeevich