3D и эффекты
perspective и preserve-3d, повороты по осям, filter и backdrop-filter, режимы наложения и появление блоков при прокрутке.
Что ты научишься делать
- Сделаешь карточку-перевёртыш на чистом CSS
- Разберёшься, что делает perspective и почему без неё нет объёма
- Настроишь появление блоков при прокрутке без единой строчки JavaScript
#perspective: откуда смотрит зритель
Поворот rotateY(45deg) без перспективы выглядит как простое сжатие: элемент просто становится уже. Объём появляется только тогда, когда браузер знает, с какого расстояния мы смотрим.
Это и задаёт perspective. Значение — расстояние от зрителя до плоскости экрана. Чем меньше число, тем ближе «камера» и сильнее искажение.
#Где писать perspective
Есть два способа, и они дают разный результат.
- Свойство
perspectiveу родителя — все дети видны из одной точки, как объекты в одной сцене. Это то, что нужно почти всегда. - Функция
perspective()внутриtransform— у каждого элемента своя камера. Элементы выглядят одинаково, независимо от положения.
.scene { perspective: 900px; perspective-origin: 50% 40%; /* откуда смотрим */}.scene__item { transition: transform .4s ease-out;}.scene__item:hover { transform: rotateY(18deg) translateZ(30px);}#preserve-3d: объём для вложенных элементов
По умолчанию дети «сплющиваются» в плоскость родителя: повернул родителя — дети повернулись вместе с ним, но собственной глубины у них нет.
transform-style: preserve-3d сохраняет объём: каждый ребёнок живёт в трёхмерном пространстве родителя. Без этого свойства не собрать ни куб, ни карточку-перевёртыш.
#Карточка-перевёртыш
Классический эффект собирается из трёх вещей: перспектива у обёртки, preserve-3d у вращающегося слоя и backface-visibility: hidden у сторон, чтобы не просвечивала изнанка.
#Фильтры
filter обрабатывает сам элемент, backdrop-filter — то, что находится под ним. Второй и даёт эффект матового стекла.
filter: blur(4px); /* размытие */filter: brightness(1.2); /* ярче */filter: grayscale(1); /* обесцветить */filter: saturate(1.4); /* насыщеннее */filter: drop-shadow(0 8px 12px rgb(0 0 0 / .3)); /* тень по форме, а не по коробке *//* Несколько сразу — через пробел */filter: grayscale(1) brightness(1.1);/* Размытие того, что под элементом */backdrop-filter: blur(14px) saturate(160%);#Режимы наложения
mix-blend-mode смешивает элемент с тем, что под ним, — как слои в графическом редакторе. Самые полезные значения: multiply (затемняет, белый становится прозрачным), screen (осветляет, чёрный становится прозрачным) и overlay.
#Появление при прокрутке — без JavaScript
Раньше для этого обязательно нужен был скрипт. Теперь у CSS есть анимации, привязанные к прокрутке: браузер сам считает, насколько элемент виден, и проигрывает анимацию соответственно.
@keyframes reveal { from { opacity: 0; transform: translateY(24px); } to { opacity: 1; transform: translateY(0); }}.card { animation: reveal linear both; /* привязываем ход анимации к появлению элемента на экране */ animation-timeline: view(); animation-range: entry 0% cover 30%;}Похожая история с View Transitions API — плавными переходами между страницами. Одна строка @view-transition { navigation: auto; } добавляет анимацию перехода там, где браузер это умеет, и ничего не делает там, где нет. Это идеальный пример прогрессивного улучшения: работает — хорошо, не работает — тоже нормально.
#will-change: обещание браузеру
will-change: transform заранее готовит элемент к анимации, вынося его на отдельный слой. Иногда это убирает подтормаживание в первый момент.
Но каждый такой слой съедает память видеокарты. Ставить will-change «на всякий случай» всем элементам — верный способ замедлить страницу вместо ускорения.
Где спотыкаются почти все
Поворот без perspective
Почему rotateY(45deg) без перспективы выглядит как обычное сжатие: объёма нет.
Как надо Задай perspective родителю-сцене. Обычно подходит 600–1200px.
Забыт transform-style: preserve-3d
Почему Дети сплющиваются в плоскость родителя, и куб или перевёртыш не собирается.
Как надо Свойство ставится вращающемуся контейнеру, а не сценe и не сторонам.
Эффект только на :hover
Почему На телефоне наведения нет вовсе, а с клавиатуры приходит фокус. Половина людей эффекта не увидит.
Как надо Добавь :focus-within и сделай элемент фокусируемым. Важную информацию не прячь за наведением.
will-change на всём подряд
Почему Каждый элемент отправляется на отдельный слой видеокарты. Память заканчивается, страница начинает тормозить.
Как надо Ставь только на то, что действительно анимируется прямо сейчас, и убирай после.
- Перспектива perspective
- Расстояние от зрителя до плоскости экрана. Чем меньше, тем сильнее эффект объёма.
- Сохранение объёма transform-style: preserve-3d
- Позволяет детям жить в трёхмерном пространстве родителя.
- Видимость изнанки backface-visibility
- Показывать ли обратную сторону повёрнутого элемента.
- Фильтр filter
- Графическая обработка элемента: размытие, обесцвечивание, тень по форме.
- Режим наложения mix-blend-mode
- Способ смешивания элемента с тем, что под ним.
- Анимация по прокрутке scroll-driven animation
- Анимация, ход которой определяется положением элемента на экране.
Проверь себя
Без точки зрения браузер рисует плоскую проекцию — выглядит как сжатие, а не поворот.
Свойство нужно тому элементу, чьи дети должны сохранить объём, — то есть вращающемуся слою.
Именно поэтому <code>backdrop-filter: blur()</code> даёт эффект матового стекла: размывается фон, а не сам элемент.
Контекст наложения из восемнадцатого урока создают <code>transform</code>, <code>filter</code>, <code>opacity</code> и ещё несколько свойств.
Неизвестное свойство игнорируется, остальная часть правила работает. Главное — не завязывать на нём видимость контента.
Задание
Карточка-перевёртыш
Собери карточку, которая переворачивается при наведении и при получении фокуса:
- обёртке —
perspective; - внутреннему слою —
transform-style: preserve-3dи переход; - сторонам —
position: absolute,inset: 0иbackface-visibility: hidden; - задней стороне — стартовый
rotateY(180deg); - переворот срабатывает и на
:hover, и на:focus-within.
Подсказка 1
Обёртке: .flip { perspective: 900px; } — без неё поворот будет плоским.
Подсказка 2
Внутреннему слою нужны position: relative, размеры 100%, preserve-3d и transition: transform 600ms.
Подсказка 3
Переворот пиши так: .flip:hover .flip__inner, .flip:focus-within .flip__inner { transform: rotateY(180deg); }
Показать решение
<div class="flip" tabindex="0"> <div class="flip__inner"> <div class="flip__face flip__face--front"> <h3>Раф на кокосовом</h3> <p>240 ₽</p> </div> <div class="flip__face flip__face--back"> <p>Кокосовое молоко, ваниль, двойной эспрессо.</p> </div> </div></div>* { box-sizing: border-box; }.flip { perspective: 900px; width: 240px; height: 160px;}.flip__inner { position: relative; width: 100%; height: 100%; transform-style: preserve-3d; transition: transform 600ms cubic-bezier(.22, 1, .36, 1);}.flip:hover .flip__inner,.flip:focus-within .flip__inner { transform: rotateY(180deg);}.flip__face { position: absolute; inset: 0; display: grid; place-content: center; padding: 18px; text-align: center; border-radius: 14px; backface-visibility: hidden;}.flip__face--front { color: #fff; background: linear-gradient(135deg, #7c5cff, #b06cff); }.flip__face--back { color: #16181d; background: #f0eefb; border: 1px solid #d9d3f5; transform: rotateY(180deg);}.flip h3 { margin: 0 0 6px; font-size: 17px; }.flip p { margin: 0; font-size: 14px; }