Анимация в CSS
Переходы учили A → B по триггеру. Transform показывал, куда уходит отрисованная box. Анимации задают линию времени: пульс индикатора, бесконечный спиннер, карточки, входящие по очереди — шаги в @keyframes, запуск через animation.
Что такое анимация
Две части: именованный набор keyframe (@keyframes) со стилями на процентах (или from / to) и правило, которое проигрывает это имя с длительностью, необязательной задержкой, числом циклов, направлением, timing function и fill-mode. Браузер интерполирует между остановками keyframe.
Имена должны совпадать строго: animation-name: pulse сработает только если есть @keyframes pulse. Переименуете одно без другого — движения не будет.
Управление анимацией — кратко
📊 Основные части animation
| Элемент | Роль |
|---|---|
@keyframes name { … } |
Линия времени значений свойств на % / from / to |
animation-name |
Какой набор keyframe проигрывать |
animation-duration |
Длина одного цикла (s или ms) |
animation-timing-function |
Сглаживание внутри цикла (ease, linear, …) |
animation-delay |
Пауза перед первым циклом |
animation-iteration-count |
Сколько циклов (number или infinite) |
animation-direction |
normal, reverse, alternate, alternate-reverse |
animation-fill-mode |
Стили до/после (none, forwards, backwards, both) |
animation shorthand |
name → duration → timing → delay → iteration → direction → fill-mode (типичный порядок) |
Не все longhand сразу нужны. Начните с name + duration; для loaderов добавьте infinite, для отката — alternate, когда последний keyframe должен остаться — forwards.
Первая линия времени — спокойный пульс
Opacity и transform щадят отрисовку. Мягкий пульс — классическое первое упражнение с @keyframes.
@keyframes pulse {
0%,
100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.55;
transform: scale(0.92);
}
}
.status-dot {
display: inline-block;
width: 0.75rem;
height: 0.75rem;
border-radius: 50%;
background: #16a34a;
animation: pulse 1.4s ease-in-out infinite;
}
0% и 100% совпадают — цикл стыкуется без рывка. infinite крутит цикл дальше — нормально для маленького индикатора, шумно, если анимировать весь hero страницы.
Бесконечное вращение — loader любит linear
Поворот с linear и infinite читается как непрерывный спиннер. Добавьте запасной вариант при prefers-reduced-motion: скрыть или заморозить вращение.
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.loader {
width: 2rem;
height: 2rem;
border: 3px solid #e5e7eb;
border-top-color: #2563eb;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
.loader {
animation: none;
border-top-color: #e5e7eb;
}
}
to — сокращение для 100%. Старт с дефолтного 0% (без transform) достаточен, когда важен только конец оборота.
Задержка и stagger — одни keyframes, разные старты
Один набор keyframe может двигать много элементов. Stagger через animation-delay: список ощущается поэтапным без трёх разных анимаций.
@keyframes fade-up {
from {
opacity: 0;
transform: translateY(0.75rem);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.reveal-list li {
opacity: 0;
animation: fade-up 0.45s ease forwards;
}
.reveal-list li:nth-child(1) { animation-delay: 0s; }
.reveal-list li:nth-child(2) { animation-delay: 0.08s; }
.reveal-list li:nth-child(3) { animation-delay: 0.16s; }
forwards удерживает финальные стили keyframe после цикла — иначе элементы могут вернуться к opacity: 0 из базового правила. Это и есть работа animation-fill-mode.
Fill-mode для одноразового появления
Входные motion часто играют один раз. Fill-mode решает, применяются ли стили до задержки и после последнего кадра.
@keyframes card-in {
from {
opacity: 0;
transform: translateY(1rem) scale(0.98);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.card-enter {
animation: card-in 0.5s ease 0.1s both;
}
Shorthand здесь: имя card-in, длительность 0.5s, easing ease, задержка 0.1s, fill-mode both. both берёт первый keyframe на время delay и держит последний после окончания.
Направление, play state и когда не анимировать
animation-direction: alternate — нечётные циклы вперёд, чётные назад; удобно для мягкого bounce без дублирования keyframes. animation-play-state: paused замораживает идущую анимацию (например, пауза декоративного цикла по hover).
Простая смена цвета по hover — по-прежнему transitions. Анимации — когда нужны циклы, многошаговый сценарий или движение не привязано к одному переключению состояния. В keyframes предпочитайте transform и opacity; width, height или top тяжелее и часто дёргают layout.
Попробуйте
- Пульс с тремя остановками (
0%/50%/100%) только по opacity; затем добавьте чутьscale. - Спиннер:
spin+linear+infinite; переключите наeaseи почувствуйте неровность. - Stagger трёх badge одними
fade-upkeyframes и задержками0s,0.1s,0.2s— сforwardsи без. - Shorthand
animation: card-in 0.5s ease 0.15s bothна панели; уберитеbothи посмотрите snap-back. - При
prefers-reduced-motion: reduceзадайтеanimation: none(или короткий fade) на декоративных циклах.
Частые ошибки
- Ждать, что hover A→B fade «зациклится» — это зона animation, а не один transition
- Опечатка между
animation-nameи именем в@keyframes— тихий no-op infiniteна крупных, навязчивых блоках без паузы или reduced-motion- Анимация layout-свойств (
width,margin,top), когда хватило бы transform/opacity - Нет
forwards/bothна одноразовых reveal сopacity: 0в базовом правиле
Итог
@keyframesзадают линию времени;animation(или longhands) её проигрывают- iteration, direction, delay и fill-mode — для циклов, отката, stagger и «липкого» финала
- Предпочитайте transform/opacity; уважайте prefers-reduced-motion на декоративном motion
- Transitions для простых смен состояния; animations для многошагового и повторяющегося
- Далее в Effects & Motion: CSS Filters
🧠 Проверьте Свои Знания
Проверьте Свои Знания
Бросьте себе вызов с помощью этой интерактивной викторины и узнайте, насколько хорошо вы понимаете тему
📝 Инструкции
- Внимательно прочитайте каждый вопрос
- Выберите лучший ответ на каждый вопрос
- Вы можете пересдавать тест столько раз, сколько захотите
- Ваш прогресс будет показан вверху