Фильтры и эффекты
Размытие, стеклянные панели и разница между drop-shadow и box-shadow, из-за которой тень вокруг прозрачной картинки выглядит неправильно.
Что ты научишься делать
- Соберёшь стеклянную панель, читаемую на любом фоне
- Поймёшь, когда нужен drop-shadow, а когда box-shadow
- Научишься оценивать цену эффекта для браузера
#Один инструмент вместо графического редактора
Свойство filter — это набор готовых обработок: размыть, осветлить, обесцветить, повернуть цвета. Раньше ради каждой из них картинку правили в редакторе и хранили вторую копию файла.
Теперь копия не нужна: наведение, тёмная тема, состояние «недоступно» — всё это варианты одного и того же изображения.
#Порядок имеет значение
Фильтры применяются по очереди слева направо, каждый следующий работает с результатом предыдущего.
filter: blur(4px) brightness(1.5) сначала размывает, потом осветляет размытое. filter: brightness(1.5) blur(4px) сначала осветляет, потом размывает уже осветлённое — результат другой.
| Функция | Что делает | Нейтральное значение |
|---|---|---|
blur(4px) | Размытие по Гауссу | 0 |
brightness(1.2) | Яркость | 1 |
contrast(1.4) | Контраст | 1 |
saturate(0.4) | Насыщенность | 1 |
grayscale(1) | Обесцвечивание | 0 |
hue-rotate(90deg) | Сдвиг цветового круга | 0deg |
drop-shadow(0 4px 8px …) | Тень по форме содержимого | — |
opacity(.5) | Прозрачность внутри стека фильтров | 1 |
#drop-shadow против box-shadow
Это разные вещи, и путаница здесь стоит дорого.
box-shadow рисует тень от прямоугольника элемента. Он ничего не знает про содержимое: у PNG с прозрачным фоном тень получится квадратной, вокруг пустоты.
filter: drop-shadow() обводит непрозрачные пиксели. Тень повторяет силуэт картинки, текст или фигуру, обрезанную через clip-path.
#backdrop-filter: стекло
filter обрабатывает сам элемент. backdrop-filter обрабатывает то, что за ним, — так делают матовые панели поверх фотографии.
Обязательное условие: элемент должен быть полупрозрачным. Через непрозрачный фон размытую подложку всё равно не видно.
#Тень у текста и иконок
У drop-shadow есть применение, о котором часто забывают: тень для текста сложной формы и для иконок-контуров.
Свойство text-shadow работает только с текстом и не умеет обводить SVG. А drop-shadow обведёт что угодно — надпись, иконку из спрайта, вырезанную фигуру, — потому что смотрит на непрозрачные пиксели, а не на тип элемента.
/* Мягкая тень делает белый текст читаемым на светлых участках */.caption { color: #fff; filter: drop-shadow(0 1px 2px rgb(0 0 0 / .8));}/* Тот же приём для иконки из спрайта */.icon--onphoto { filter: drop-shadow(0 1px 3px rgb(0 0 0 / .7));}#Цена эффекта
Фильтры считаются на видеокарте, но не бесплатно. Особенно дороги размытия: браузер обрабатывает каждый пиксель области несколько раз.
| Что делаем | Цена |
|---|---|
grayscale, brightness, saturate на небольшом элементе | Дёшево |
blur на иконке или кнопке | Терпимо |
blur на полноэкранном блоке | Дорого |
backdrop-filter на липкой шапке при прокрутке | Дорого: пересчёт на каждом кадре |
Анимация filter | Очень дорого — фильтр считается заново каждый кадр |
#SVG-фильтры: когда CSS не хватает
В CSS десяток готовых функций. Если нужного эффекта среди них нет — например, зернистость, свечение или растекание — его собирают в SVG из примитивов и подключают по ссылке на идентификатор.
Выглядит это громоздко, зато фильтр остаётся частью страницы: его можно менять переменными и он не требует ни одного файла изображения.
<svg width="0" height="0" aria-hidden="true"> <filter id="grain"> <!-- Генерируем шум --> <feTurbulence type="fractalNoise" baseFrequency="0.8" numOctaves="3"/> <!-- И приглушаем его --> <feColorMatrix type="saturate" values="0"/> <feComponentTransfer> <feFuncA type="linear" slope="0.35"/> </feComponentTransfer> </filter></svg>.hero::after { content: ""; position: absolute; inset: 0; filter: url(#grain); mix-blend-mode: overlay; pointer-events: none;}#Как это выглядит в реальном проекте
@layer components { /* Липкая шапка. Размытие — только когда страница прокручена */ .header { position: sticky; inset-block-start: 0; background: color-mix(in oklab, var(--bg) 86%, transparent); } .header.is-scrolled { backdrop-filter: blur(14px) saturate(140%); -webkit-backdrop-filter: blur(14px) saturate(140%); } /* Недоступный товар: обесцвечен, но остаётся читаемым */ .product[data-out-of-stock] .product__media { filter: grayscale(1) contrast(.9); } /* Логотипы партнёров: одноцветные, цвет проявляется при наведении */ .partners img { filter: grayscale(1) opacity(.65); transition: opacity .25s ease; } .partners a:hover img, .partners a:focus-visible img { /* Меняем только прозрачность — фильтр остаётся прежним */ opacity: 1; }}#Когда НЕ надо
- Прятать содержимое размытием. Размытый текст остаётся в разметке и читается программой чтения с экрана; выделить и скопировать его тоже можно. Для скрытия данных это не годится.
- Стекло на пёстром фоне без подложки. Панель будет выглядеть эффектно на скриншоте и станет нечитаемой на настоящей фотографии.
- Анимировать filter. Каждый кадр считается заново. Анимируй
opacityотдельного слоя. - Чинить фильтром плохую картинку.
contrastиsaturateне вернут детали, которых нет в файле. Это косметика, а не восстановление: пересохранить исходник в нужном размере всегда лучше.
#Поддержка браузерами
| Возможность | Состояние |
|---|---|
filter с функциями | Во всех браузерах, давно |
filter: url(#id) из SVG | Во всех современных браузерах |
backdrop-filter | Поддерживается везде; в части сборок WebKit нужен префикс -webkit- |
drop-shadow | Во всех браузерах |
/* База: плотная подложка, читается всегда */.panel { background: rgb(255 255 255 / .92); }/* Улучшение: там, где размытие есть, подложку делаем легче */@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) { .panel { background: rgb(255 255 255 / .3); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); }}Где спотыкаются почти все
Размылась карточка вместе с текстом
Почему filter действует на элемент и всех потомков — исключений нет.
Как надо Вешай фильтр на псевдоэлемент или отдельный слой под содержимым.
Квадратная тень вокруг прозрачного PNG
Почему box-shadow рисует тень от прямоугольника элемента и не смотрит на содержимое.
Как надо Для силуэта нужен filter: drop-shadow().
backdrop-filter не работает
Почему Чаще всего фон элемента непрозрачный: размытие есть, но его не видно. Иногда мешает overflow: hidden у предка.
Как надо Сделай фон полупрозрачным и проверь, что предок не обрезает слой.
Анимация filter тормозит
Почему Фильтр пересчитывается на каждом кадре, а размытие — самая дорогая из операций.
Как надо Держи два слоя и анимируй opacity размытого.
- Фильтр filter
- Обработка элемента и его содержимого: размытие, яркость, обесцвечивание.
- Фильтр подложки backdrop-filter
- Обработка того, что находится позади элемента.
- Тень по силуэту drop-shadow
- Тень, повторяющая непрозрачные пиксели, а не рамку элемента.
- SVG-фильтр SVG filter
- Собственный эффект, собранный из примитивов вроде
feTurbulence.
Проверь себя
Исключений нет. Чтобы размыть только фон, фильтр вешают на отдельный слой или псевдоэлемент.
<code>box-shadow</code> рисует тень от прямоугольника. Силуэт обводит только <code>drop-shadow</code>.
Через непрозрачный фон размытую подложку не видно. Второе по частоте — <code>overflow: hidden</code> у предка.
Прозрачность браузер меняет почти бесплатно, а фильтр пересчитывает каждый кадр.
Каждый следующий фильтр работает с результатом предыдущего. Осветлить и размыть — не то же, что размыть и осветлить.
Задание
Стеклянная панель, читаемая всегда
На пёстром фоне лежит панель с текстом — сейчас его почти не разобрать.
Сделай панель стеклянной: добавь backdrop-filter с размытием и повышенной насыщенностью, полупрозрачную светлую подложку и светлую рамку. Не забудь префиксную строку для WebKit.
Отдельно: у звезды сейчас box-shadow, и после обрезки тени не видно. Замени на тень по силуэту.
Подсказка 1
Стекло: backdrop-filter: blur(12px) saturate(140%); и та же строка с префиксом -webkit-.
Подсказка 2
Подложка обязательна: background: rgb(255 255 255 / .3); плюс border: 1px solid rgb(255 255 255 / .45);
Подсказка 3
Тень по силуэту вешается на родителя: .star-wrap { filter: drop-shadow(0 8px 10px rgb(20 24 48 / .5)); }
Показать решение
<div class="scene"> <div class="glass"> <h3>Кофейня «Полка»</h3> <p>Этот текст должен читаться на любом участке фона.</p> </div> <div class="star-wrap"> <div class="star"></div> </div></div>.scene { position: relative; padding: 26px; border-radius: 14px; background: radial-gradient(circle at 18% 22%, #ffd166 0 15%, transparent 16%), radial-gradient(circle at 80% 72%, #4bd1a0 0 18%, transparent 19%), linear-gradient(135deg, #6544e0, #f2777a);}.glass { padding: 16px; border-radius: 12px; color: #16181d; -webkit-backdrop-filter: blur(12px) saturate(140%); backdrop-filter: blur(12px) saturate(140%); background: rgb(255 255 255 / .3); border: 1px solid rgb(255 255 255 / .45);}.glass h3 { margin: 0 0 6px; font-size: 17px; }.glass p { margin: 0; font-size: 14px; }.star-wrap { margin-top: 18px; filter: drop-shadow(0 8px 10px rgb(20 24 48 / .5));}.star { width: 70px; height: 70px; background: #fff; clip-path: polygon(50% 0, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);}