CSS-анимации: от перехода до скролл-эффектов
Анимация превращает статичную страницу в живой интерфейс: кнопки отзываются на действия, карточки плавно появляются, а контент не «прыгает» между состояниями. Начнём с переходов и кривых скорости, разберём @keyframes, научимся анимировать по скроллу и поговорим о производительности и доступности.
1. Зачем анимировать
Анимация — это не украшение, а инструмент коммуникации. Она подсказывает пользователю, что элемент отреагировал на действие, откуда пришло новое содержимое и что происходит в системе прямо сейчас.
- Отклик на действия — кнопка нажалась, переключатель передвинулся, ссылка подсветилась. Пользователь видит, что его действие принято.
- Ориентация — панель выехала сбоку, вкладка сместилась, карточка улетела из списка. Движение показывает, откуда и куда движется контент.
- «Живой» интерфейс — индикатор загрузки, pulse-точка уведомления и мигающий курсор объясняют состояние без единого слова.
2. transition: базовые переходы
Свойство transition плавно переводит элемент из одного состояния в другое при изменении значения: по наведению, фокусу или смене класса. Переход применяется к разнице между двумя наборами CSS-свойств.
.button {
background: #4f7cff;
transition-property: background, transform;
transition-duration: 0.2s;
transition-timing-function: ease-out;
transition-delay: 0s;
}
.button:hover {
background: #2f5ce0;
transform: translateY(-2px);
}Короткая запись объединяет все четыре части:
transition: background 0.2s ease-out, transform 0.15s ease-out;| Свойство | Что задаёт |
|---|---|
| transition-property | какие свойства анимируются |
| transition-duration | длительность перехода |
| transition-timing-function | кривая скорости внутри интервала |
| transition-delay | пауза перед началом |
transition: all .3s: анимироваться будет всё подряд, замедляя страницу и отключая осознанные решения. Перечисляйте свойства явно.3. Кривые скорости
transition-timing-function (и его аналог animation-timing-function) задаёт, как скорость меняется в течение анимации. Одни кривые разгоняются, другие тормозят в конце, третьи — «пружинят», возвращаясь чуть дальше цели.
| Ключевое слово | Поведение |
|---|---|
| linear | постоянная скорость, без разгона |
| ease | значение по умолчанию: плавный разгон и торможение |
| ease-in | медленный старт, ускорение к концу |
| ease-out | быстрый старт, плавное торможение — любимец интерфейсов |
| ease-in-out | симметричный разгон и торможение |
| cubic-bezier() | произвольная кривая через четыре точки |
Функция cubic-bezier(x1, y1, x2, y2) описывает кривую Безье двумя контрольными точками. Первые две координаты ограничены отрезком от 0 до 1, вторые могут выходить за него — так получается «отскок назад».
/* мягкий выход — универсальный ease-out для интерфейсов */
transition: transform 0.3s cubic-bezier(0.2, 0, 0, 1);
/* «пружина»: элемент перелетает цель и возвращается */
transition: transform 0.45s cubic-bezier(0.34, 1.56, 0.64, 1);cubic-bezier(0.2, 0, 0, 1) и «магические» кривые из easing-сборников сэкономят часы подбора. Запомните пару любимых и используйте их везде.Сравните кривые на живом примере: наведите курсор на ряд, и квадраты пролетят до конца с разными скоростями.
4. transform и opacity: золотая пара
transform и opacity — единственные свойства, которые браузер может анимировать без пересчёта вёрстки. Элемент выносится на отдельный слой, и движение выполняет графический композитор на GPU: никаких пошаговых пересчётов layout и paint.
.card {
transform-origin: center;
transition: transform 0.25s ease-out;
}
.card:hover {
transform: translateY(-6px) scale(1.04);
}
.icon {
transition: transform 0.2s ease-out;
}
.icon:hover {
transform: rotate(90deg);
}transform покрывает движение, поворот, масштаб и наклон: translate(), rotate(), scale(), skew(). transform-origin задаёт точку, вокруг которой происходят повороты и масштабирование — по умолчанию это центр.
margin, width, height, top, left пересчитывает layout на каждом кадре — это дорого и заметно «рвётся» на слабых устройствах. Перемещайте элементы только через transform: translate().5. @keyframes: ключевые кадры
Если переход плавно меняет одно состояние на другое, то @keyframes описывает целый сценарий из нескольких кадров — от появления до «парения» и обратно. Кадры задаются ключевыми словами from/to или процентами.
@keyframes fade-up {
from { opacity: 0; transform: translateY(16px); }
to { opacity: 1; transform: translateY(0); }
}
.hero {
animation: fade-up 0.6s ease-out both;
animation-delay: 0.15s;
}Короткая запись: animation принимает имя, длительность, функцию скорости, количество повторений и задержку одним набором значений. Гибкие кадры через проценты:
@keyframes float {
0% { transform: translateY(0); }
50% { transform: translateY(-12px); }
100% { transform: translateY(0); }
}
.badge {
animation: float 3s ease-in-out infinite;
}| fill-mode | Поведение до и после анимации |
|---|---|
| none | элемент в естественном состоянии (по умолчанию) |
| forwards | остаётся в состоянии последнего кадра после конца |
| backwards | применяет первый кадр ещё до старта (со задержкой) |
| both | комбинация backwards и forwards |
both: без него элемент может пару кадров мелькнуть в конечном состоянии до запуска анимации, особенно с animation-delay.6. Повторение и итерации
По умолчанию анимация проигрывается один раз. animation-iteration-count задаёт число повторов, а infinite — бесконечный цикл. Двусторонний цикл задаётся направлением alternate: элемент движется вперёд и назад, а не прыгает в начальную точку.
.icon-spinner {
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.pulse {
animation: pulse 1.6s ease-in-out infinite alternate;
}
@keyframes pulse {
from { opacity: 0.35; }
to { opacity: 1; }
}Интерактивная пауза по наведению делается через animation-play-state:
.ticker:hover {
animation-play-state: paused;
}animation-iteration-count: 2— два полных прохода;alternate— чередование направления; часто сочетается сinfinite;animation-play-state: paused— остановка в текущем кадре.
7. Микроанимации интерфейса
Микроанимации — маленькие движения, которые делают интерфейс отзывчивым: подъём карточки по наведению, пульсирующая точка уведомления, спиннер, поворачивающаяся иконка. Общий рецепт: 150–300 миллисекунд, transform и opacity, тихая кривая ease-out.
.card {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
transition: transform 0.25s ease-out, box-shadow 0.25s ease-out;
}
.card:hover {
transform: translateY(-6px);
box-shadow: 0 10px 22px rgba(0, 0, 0, 0.15);
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(35, 197, 106, 0.5); }
100% { box-shadow: 0 0 0 14px rgba(35, 197, 106, 0); }
}
.dot {
width: 12px;
height: 12px;
border-radius: 50%;
background: #23c56a;
animation: pulse 1.4s ease-out infinite;
}Посмотрите три готовых микроанимации вживую: подъём карточки, pulse-индикатор и спиннер.
8. Появление контента
Появление — самый частый сценарий новых разделов: fade-in со скольжением вверх читается мягче, чем резкое появление. Каскад с задержками (stagger) делает списки «ожившими»: каждый следующий элемент появляется с небольшой паузой после предыдущего.
@keyframes fade-up {
from { opacity: 0; transform: translateY(16px); }
to { opacity: 1; transform: translateY(0); }
}
.list-item {
opacity: 0;
animation: fade-up 0.4s ease-out forwards;
}
.list-item:nth-child(2) { animation-delay: 0.12s; }
.list-item:nth-child(3) { animation-delay: 0.24s; }
.list-item:nth-child(4) { animation-delay: 0.36s; }opacity: 0 до запуска анимации: если CSS вдруг не загрузится или пользователь отключит анимации (раздел 9), контент останется невидимым. Держите видимое состояние в зоне, отвечающей за «после анимации» через forwards.9. Уважаем пользователя: prefers-reduced-motion
Часть пользователей страдает от движения — укачивание, вестибулярные расстройства, мигрени. Настройка ОС «Уменьшить движение» отправляет медиазапрос prefers-reduced-motion, и сайт обязан на него откликнуться.
.hero { animation: fade-up 0.6s ease-out; }
@media (prefers-reduced-motion: reduce) {
.hero,
.list-item {
animation: none;
opacity: 1;
transform: none;
}
}Плюс общее правило безопасности для всей страницы: отключить декоративные анимации одним блоком — но оставить обязательные индикаторы, которые несут информацию.
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}Проверьте, как блок ведёт себя с включённым «уменьшением движения» в вашей системе.
10. Анимации по скроллу
Новейшая возможность CSS: animation-timeline: view() и scroll() привязывают ход анимации к прокрутке. Элемент «появляется» по мере въезда во вьюпорт, а прогресс-бар растёт вместе с прокруткой страницы — без единой строчки JavaScript.
@supports (animation-timeline: view()) {
.reveal {
animation: fade-up both;
animation-timeline: view();
animation-range: entry 10% cover 35%;
}
.progress {
transform-origin: left;
animation: grow linear both;
animation-timeline: scroll(root);
}
}
@keyframes grow {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}view() связывает кадры с пересечением элемента и вьюпорта, scroll(root) — с прокруткой всей страницы. animation-range уточняет, в какой момент пути анимация стартует и завершается. Для параллакс-фрагментов сдвигайте фон через transform: translateY() внутри той же timeline.
@supports. В старых браузерах элемент просто останется в конечном состоянии — это надёжный fallback. Обходной путь на JavaScript — библиотеки, превращающие появление по скроллу.11. Производительность
Рендер каждого кадра проходит три стадии: layout (пересчёт геометрии), paint (заливка пикселей) и composite (сборка слоёв). Анимация transform/opacity минует первые две и работает только на композиторе — это главный секрет плавных 60 кадров в секунду.
.element {
/* предупреждаем браузер: свойство будет меняться */
will-change: transform;
}
.hidden-section {
/* вне экрана не тратим время на отрисовку */
content-visibility: auto;
contain-intrinsic-size: 0 400px;
}| Стадия | Что происходит | Что вызывает |
|---|---|---|
| Layout | пересчёт размеров и позиций | margin, width, height, top, left |
| Paint | заливка пикселей | box-shadow, background, color |
| Composite | сборка готовых слоёв | transform, opacity |
- анимируйте только
transformиopacity; will-changeприменяйте точечно и снимайте после анимации — постоянная запись съедает память;content-visibility: autoскрывает отрисовку ниже экрана;- проверяйте график кадров во вкладке Performance в Chrome DevTools.
top/left: каждый кадр пересчитывает layout и может обрушить производительность до нескольких кадров в секунду на слабых устройствах.12. Готовые приёмы и библиотеки
Для частых сценариев не нужно изобретать велосипед: почти всё закрывают готовые подходы и коллекции. Но библиотека — это вес, JavaScript и зависимость от чужих обновлений, поэтому прикидывайте, что дешевле.
- ключевые кадры руками — появление, hover-подъём, пульс, спиннер пишутся в 10–15 строк;
- easing-утилиты — готовые сборники кривых вроде easing.net и easings.net подсказывают cubic-bezier под задачу;
- AOS и scroll-reveal — появление блоков по скроллу по атрибуту
data-aos; - GSAP или Motion One — мощные движки для сложных постановочных сцен, когда нужно управление кадрами из JavaScript;
- Cubic-bezier генераторы — подбор кривой мышкой в DevTools: панель transitions.
13. Чек-лист перед сдачей
Перед релизом прогоните анимации по контрольному списку. Он закрывает и доступность, и производительность, и аккуратность восприятия.
- ☐ анимации длительностью 150–300 мс, дольше — только там, где это оправдано;
- ☐ движутся только
transformиopacity, никакого margin/width/top; - ☐
prefers-reduced-motionучтён: декоративные движения отключаются; - ☐ обязательные индикаторы (загрузка, предупреждение) остались видимыми;
- ☐ нет анимации-декора на каждом блоке — движение только там, где оно объясняет действие;
- ☐ hover/focus-состояния видны сразу, без задержки, на клавиатуре тоже;
- ☐ на слабом устройстве проверена плавность — в DevTools включён эмулятор с низким FPS.
Что дальше
Ты научился оживлять интерфейс: переходы, keyframes, scroll-анимации, доступность и производительность. Закрепи знания по вёрстке на курсе CSS, прокачай раскладку в Flexbox и Grid и собери всё вместе в практике: лендинг.