Эффект размытия вместо тени на CSS

alexei27/04/2024 - 09:06
Эффект размытия вместо тени на CSS

С тенями в CSS все довольно просто: объявляете свойство box-shadow, задаете смещения тени, определяете размытие и задаете цвет. Это отличный способ придать глубину тому, что в остальном было бы плоским дизайном! Черпая вдохновение в тенях, автор статьи Яир Эвен Ор (Yair Even Or) создает нечто подобное, только с эффектом размытия вместо тени. В этой статье вы получите пошаговое объяснение того, как это делается с помощью комбинации масок, градиентов и старого доброго свойства backdrop-filter.

Представьте, что мы используем свойство box-shadow, но только для создания эффекта размытия, когда фон элемента размывается вокруг этого элемента, постепенно уменьшая силу размытия. Мне пришла в голову эта идея, когда я пытался улучшить контрастность всплывающего окна над темной областью. Там box-shadow с точки зрения веб-дизайна не имела никакого смысла. Тогда я подумал, а какими еще способами можно подчеркнуть тот или иной элемент? И вдруг мне пришла в голову мысль, а почему бы не выделить нужный элемент при помощи идея эффекта размытия?

See the Pen Faded Outer Box Backdrop Blur - Simplified Version by Alexei Goloviznin on .

Было бы здорово, если бы существовало какое-нибудь свойство типа box-blur или какое-нибудь ключевое слово blur, которое мы могли бы использовать с box-shadow, как мы это делаем для внутренних теней. К сожалению, у CSS нет такого свойства. Но поскольку CSS потрясающе гибкий инструмент, мы все равно можем получить подобный эффект, объединив несколько CSS функций и используя их некоторые дополнительные возможности.

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

Начнем с разметки

Технически эффект будет создаваться для псевдоэлемента ::before некоторого элемента, скажем, всплывающего или диалогового окна, всплывающей подсказки и т.п. Обычно именно такие элементы дизайна являются "целями" для такого рода эффектов. Думаю, что использование здесь псевдоэлемента является хорошим подходом, потому что в таком случае мы можем привязать стили к псевдоэлементу и затем переназначать их для других элементов без каких-либо изменений HTML.


<!-- По сути это все что нужно для данного демо -->
<div></div>

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

Позиционирование псевдоэлемента

Нам нужно, чтобы псевдоэлемент ::before занимал всю область нашего элемента <div>. Но кроме этого, нам нужно, чтобы наш псевдоэлемент также выходил за нее, так как это создает видимую область, в которой и создается эффект размытия, распространяющийся за пределы элемента.


::before {  
  content: '';

  /* Убедитесь, что родительский элемент по крайней мере имеет относительное позиционирование. */
  position: absolute;
  
  /* Размер эффекта должен быть меньше 0, чтобы он распространялся наружу. */
  inset: -100px;

  /* Этот слой расположен между родительским элементом и фоном страницы. */
  /* Убедитесь, что это значение меньше значения `z-index` родительского элемента. */
  z-index: -1;
}

В комментариях к коду указаны ключевые элементы. Так, чтобы псевдоэлемент ::before отображался в браузере, в свойстве content должна быть задана пустая строка, затем мы удаляем его из потока вывода, придавая ему абсолютное позиционирование. Это позволяет нам задать положение элемента и, в конечном итоге, задавать направления эффекта размытия так же, как и в свойстве box-shadow, только мы для управления его размером используем свойство inset. Для свойства inset нам нужно отрицательное значение, причем эффект будет тем сильнее, чем меньше это значение.

До сих пор мы закладывали основу для нашего эффекта. Пока что смотреть особо не на что. А теперь начинается самое интересное!

Маскирование при помощи прозрачных градиентов

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

Прозрачность - ключевой момент, когда дело доходит до маскирования при помощи градиентов. Прозрачность позволяет нам постепенно скрывать части элемента таким образом, чтобы создавалась иллюзия постепенного исчезновения из вида.

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

Мы будем использовать два градиента: один горизонтальный, а другой вертикальный. Я выбрал этот путь, потому что он имитирует грубую прямоугольную форму, которая исчезает к краям.

Как я уже говорил, прозрачность - это ключ к решению нашей задачи. Оба градиента начинаются с прозрачности (transparent), затем переходят к черному (black) до самого конца, где они возвращаются к прозрачности (transparent) для исчезновения из виду. Помните, что эти градиенты на самом деле маски, а не фоновые изображения, поэтому они объявлены в свойстве mask, которое определяет, какие пиксели должны быть отображены, и их прозрачность.


mask:
  linear-gradient(to top, transparent 0%, black 25% 75%, transparent 100%),
  linear-gradient(to left, transparent 0%, black 25% 75%, transparent 100%);

See the Pen Basic Gradient Mask by Alexei Goloviznin on CodePen.

  • Вертикальный градиент (to top) создает переход от прозрачного внизу к черному посередине, а затем обратно к прозрачному вверху.
  • Горизонтальный градиент (to left) меняет цвет с прозрачного справа на черный посередине, а затем снова становится прозрачным слева.

Этот двойной градиент размещает черные области таким образом, что они сливаются, создавая грубую базовую конструкцию прямоугольной формы, которую на следующем шаге мы настроим более точно. Свойство mask лучше всего объявлять сначала с префиксом, а затем без префикса, чтобы обеспечить большую поддержку браузеров:


-webkit-mask:
  linear-gradient(to top, transparent 0%, black 25% 75%, transparent 100%),
  linear-gradient(to left, transparent 0%, black 25% 75%, transparent 100%);
mask:
  linear-gradient(to top, transparent 0%, black 25% 75%, transparent 100%),
  linear-gradient(to left, transparent 0%, black 25% 75%, transparent 100%);

Доработка с помощью свойства mask-composite

Свойство mask-composite является частью модуля маскирования CSS и позволяет по-пиксельно контролировать смешиванием маскированного содержимого, создавая тем самым сложные композиции.

Значение source-in этого свойства очень пригодится для создания нужного нам эффекта, потому что оно указывает браузеру сохранять только перекрывающиеся области маски. В результате будут отображаться только пиксели, содержащие оба (упомянутых выше) градиента. Это фиксирует прямоугольную форму, которую затем можно применить к любому элементу DOM, у которого нет умеренно изогнутых углов (border-radius).

Постепенное размывание фона

Теперь, когда у нас есть готовая маска, все, что нам нужно сделать, это использовать ее. CSS свойство backdrop-filter может размывать все, что отображается "позади" элемента, используя функцию blur():


::before {
  /* и т.д. */

  backdrop-filter: blur(10px);
}

Чем больше значение, тем интенсивнее размытие. Я решил использовать 10px просто так. Фактически, мы можем изменять это позже, чтобы сделать реализацию еще более гибкой и легко настраиваемой.

И не забываем специально для Safari используем версию с префиксом:


::before {
  /* и т.д. */

  -webkit-backdrop-filter: blur(10px); /* Обязательно для Safari */
  backdrop-filter: blur(10px);
}

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

Легкое касание полутени

Думаю, что добавление небольшой полупрозрачной стандартной тени box-shadow, покрывающей область размытия, придаст эффекту немного дополнительной глубины. Единственное, ее следует добавлять к самому элементу, а не к псевдоэлементу ::before:


div {
  box-shadow: 0 0 40px #00000099;
}

Однако это совершенно необязательно.

Сведем все вместе

Вот так будет выглядеть код CSS, после того, как мы сведем все сказанное выше вместе:


/* Устанавливаем на псевдоэлемент ::before нужного элемента. */
::before {    
  content: '';

  /* Этот слой позиционируется между элементом и его фоном. */
  position: absolute;
  
  /* Это не должно влиять на контент контейнера. */
  z-index: -1;
  
  /* Размер размытия должен быть меньше 0, чтобы оно выходило за пределы элемента. */
  inset: -100px;
  
  /* Эффект размытия */
  -webkit-backdrop-filter: blur(10px); /* Обязательно для Safari */
  backdrop-filter: blur(10px);
  
  /* Маска притушевывает эффект размытия, так он ослабляется ближе к краям. */
  /* (Цвет заполнения неважен, значение "red" используется из-за короткости самого слова.) */
  mask: 
    linear-gradient(
      to top, 
      transparent 0%,
      red 100px calc(100% - 100px),
      transparent 100%), 
    linear-gradient(
      to left,
      transparent 0%,
      red 100px calc(100% - 100px),
      transparent 100%);
  
  /* Это объединяет предыдущие маски, таким образом отрисовываются только перекрывающиеся пиксели. */
  /* Это создает иллюзию затухающей маски. */
  mask-composite: intersect;
  -webkit-mask-composite: source-in; /* Обязательно для Safari */
}

Еще раз финальное демо

See the Pen Faded Outer Box Backdrop Blur - Simplified Version by Alexei Goloviznin on .

Ниже пример с более сложной версией данного эффекта, которая позволяет на лету поэкспериментировать с различными значениями CSS кода:

See the Pen Faded Outer Box Backdrop Blur by Alexei Goloviznin on CodePen.