Pull to refresh

Липкий эффект

Reading time 5 min
Views 31K
Original author: Lucas Bebber
Буквально недавно Крис написал про «Эффект капельного преобразования в CSS». Эффект реально крутой и сама техника реализована по-умному, но данный подход через обычные CSS фильтры имеет определенные недостатки: нельзя использовать непрозрачность, добавлять контент внутрь капель, проблемы с фоновыми цветами.

В последние дни я достаточно много экспериментировал с SVG фильтрами и заметил, что с их помощью можно решить вышеописанные проблемы в CSS реализации. Посмотрите на липкое меню, которое я сделал для демонстрации:

CodePen



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. Полные перечень этих примитив находится тут.

Взглянем на парочку примеров:

CodePen


<filter id="blur">
  <feGaussianBlur in="SourceGraphic" stdDeviation="3" />
</filter>


Этот простой фильтр выполняет трех-пиксельное размытие элемента. Важно обратить внимание на атрибут in=«SourceGraphic». В in определяется к чему именно будет применяться фильтр. Значение SourceGraphic возвращает оригинальный элемент. Таким образом мы указываем, что размытие должно происходить для оригинального графического объекта. Все достаточно просто.

Давайте рассмотрим пример немного посложнее: drop shadow. В нем наглядно показано как работает цепочка фильтровых приметив вместе:

CodePen


<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


Однако, мы по прежнему имеем:

  1. Проблемы в работе с разным цветами.
  2. Размытие производится для всего элемента, включая содержимое.
  3. Невозможность использовать непрозрачность.


Все это не позволяет применить данный трюк в реальном проекте.

С помощью 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>


Коротко о том, что мы сделали:

  1. Во-первых мы произвели размытие в 10 пикселей и присвоим имя данному результату.
  2. Затем для получившегося результата мы применили color matrix filter для увеличения контрастности альфа-канала.
  3. И после этого мы вставили оригинальным графический объект в этот эффект.


О 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"


Демо

В итоге задуманный нами эффект работает. Пример:

CodePen


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

Подытожим

  • Фильтр должен применять к контейнеру с элементами, а не к самим элементам.
  • Из за самого тянущего эффекта контейнер должен быть немного большей области чем содержимое. В противном случае вы можете получить подобные дефекты на краях:
    image
  • Для того, чтобы применять данный фильтр к примеру для прямоугольников мы должны воспользоваться немного более изощренным способом. Вместо отрисовки оригинального объекта над липкоим эффектом, мы должны применить 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.
Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
+35
Comments 16
Comments Comments 16

Articles