CSS-анимации: полное руководство для начинающих

CSS-анимация задает элементу промежуточные состояния между началом и концом. Вы опишете кадры в @keyframes, подключите их через animation и сразу увидите движение в браузере.

Первая анимация через @keyframes

Начните с небольшого блока, который перемещается на 120 пикселей вправо и возвращается назад. Такой пример показывает связь между ключевыми кадрами и свойством animation.

<div class="demo-box"></div>

<style>
.demo-box {
    width: 64px;
    height: 64px;
    border-radius: 12px;
    background: #6c5ce7;
    animation: slide 2s ease-in-out infinite alternate;
}

@keyframes slide {
    from {
        transform: translateX(0);
    }
    to {
        transform: translateX(120px);
    }
}
</style>

Сохраните разметку в HTML-файле и откройте его в браузере. Фиолетовый блок будет плавно двигаться между двумя положениями.

В объявлении animation имя slide связывает элемент с правилом @keyframes slide. Значение 2s задает длительность одной итерации, а infinite повторяет движение бесконечно.

Свойство transform меняет положение без перестройки геометрии соседних элементов.

Свойство animation и его подсвойства

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

animation-name: slide;
animation-duration: 2s;
animation-timing-function: ease-in-out;
animation-delay: 0s;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-fill-mode: both;
animation-play-state: running;

Укажите только имя и длительность, чтобы получить однократный запуск. Остальные параметры браузер возьмет из значений по умолчанию.

Пауза и возобновление

Свойство animation-play-state принимает значения running и paused. При паузе браузер сохраняет текущую позицию и продолжает движение с нее.

.demo-box {
    animation-play-state: paused;
}

.demo-box:hover {
    animation-play-state: running;
}

Добавьте эти правила после стилей первого примера. Наведите указатель на блок, чтобы возобновить движение. Уберите указатель, чтобы снова поставить его на паузу.

Для управления без указателя используйте кнопки с обработчиками JavaScript.

Задержка и отрицательное смещение

Свойство animation-delay откладывает запуск на указанное время.

.demo-box {
    animation-delay: 1s;
}

При animation-fill-mode: none или forwards во время задержки действуют обычные стили элемента. Значения backwards и both применяют начальный кадр уже во время ожидания. Начальный кадр зависит от направления воспроизведения.

Схема задержки перед началом CSS-анимации

Отрицательная задержка запускает анимацию сразу, но со смещением по времени. Например, -0.25s показывает состояние, до которого анимация дошла бы за четверть секунды.

.demo-box {
    animation-delay: -0.25s;
}
Схема запуска CSS-анимации с отрицательной задержкой

Удалите правила паузы перед проверкой задержки, затем перезагрузите страницу. Положительная задержка сработает перед первой итерацией, но не между повторами.

Сохранение состояния после завершения

После завершения анимации свойства из кадров обычно возвращаются к обычным значениям элемента. animation-fill-mode меняет это поведение.

ЗначениеЧто происходит
noneКадры действуют только во время анимации.
forwardsПосле завершения сохраняется конечное состояние с учетом направления и числа итераций.
backwardsВо время задержки применяются значения начального кадра с учетом направления.
bothНачальный кадр действует во время задержки, а конечное состояние сохраняется после завершения.

Для проверки замените объявление animation в первом примере:

.demo-box {
    animation: slide 2s ease-in-out 1s 1 reverse both;
}

Блок начнет ожидание в положении 100%, смещенном на 120 пикселей. Затем он пройдет путь в обратную сторону за 2 секунды. После завершения both сохранит кадр 0%.

При normal и одной итерации сохраняется кадр 100%. При alternate и двух итерациях сохраняется кадр 0%: второй проход идет назад. При дробном числе итераций конечное состояние может оказаться между кадрами.

Состояние элемента во время задержки CSS-анимации
Состояние элемента после завершения CSS-анимации

Направление воспроизведения

Свойство animation-direction определяет направление каждой итерации.

ЗначениеЧто происходит
normalКаждая итерация идет от 0% к 100%.
reverseКаждая итерация идет от 100% к 0%.
alternateПервая итерация идет вперед, следующая – назад, затем направление чередуется.
alternate-reverseПервая итерация идет назад, следующая – вперед, затем направление чередуется.

Например, animation-direction: alternate возвращает блок без скачка между итерациями. Значения разобраны в документации MDN по animation-direction.

Сокращенная запись animation

В сокращенной записи первое значение времени с единицей измерения задает animation-duration, второе – animation-delay. Остальные значения можно ставить в любом порядке, если браузер однозначно определяет их тип.

.demo-box {
    animation: slide 2s ease-in-out 0.5s infinite alternate both paused;
}

Здесь длительность равна 2 секундам, задержка – 0,5 секунды. Анимация повторяется бесконечно и начинается на паузе.

Свойство animation-play-state входит в сокращенную форму CSS Animations, поэтому отдельная строка для него не обязательна.

Ключевые кадры и функции времени

Правило @keyframes описывает состояния элемента на шкале от 0% до 100%. Выберите проценты для промежуточных точек или используйте from и to.

@keyframes pulse {
    0%, 100% {
        transform: scale(1);
        opacity: 0.7;
    }
    50% {
        transform: scale(1.15);
        opacity: 1;
    }
}

Ключевые слова from и to равны 0% и 100%. В одном кадре можно перечислить несколько селекторов через запятую, как в примере для начального и конечного состояния.

Неанимируемые свойства внутри @keyframes браузер просто игнорирует. Полный список можно посмотреть в справочнике анимируемых CSS-свойств на MDN.

Функция времени: animation-timing-function

Функция времени меняет скорость перехода между соседними кадрами. Она не меняет длительность анимации, только распределяет движение внутри интервала.

  • linear – сохраняет одинаковую скорость.
  • ease-in – медленно начинает движение.
  • ease-out – медленно завершает движение.
  • ease-in-out – замедляет начало и конец.
@keyframes slide {
    from {
        transform: translateX(0);
        animation-timing-function: ease-in;
    }
    to {
        transform: translateX(120px);
    }
}

Функция в кадре действует на переход к следующему кадру. Настройка animation-timing-function в кадре 100% не задает следующий переход.

Функции steps() и cubic-bezier()

steps() делит движение на отдельные скачки. Функция подходит для счетчиков, кадровой графики и мигания.

Для проверки замените объявление анимации у видимого блока первого примера:

.demo-box {
    animation: slide 4s steps(4, end) infinite;
}

Блок пройдет 120 пикселей четырьмя скачками по 30 пикселей. Используйте исходные кадры slide без функции времени внутри них.

cubic-bezier() задает собственную кривую ускорения четырьмя числовыми параметрами. Первое и третье значения должны лежать между 0 и 1.

.demo-box {
    animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
}

Берите готовые значения из документации или подбирайте кривую в инструментах разработчика. Для первой анимации хватит ease, linear и steps().

Повторное использование и несколько анимаций

Один набор ключевых кадров можно подключить к нескольким элементам.

Сохраните этот самостоятельный пример в отдельном HTML-файле. Размеры и фон сделают оба пустых элемента видимыми.

<div class="large-box"></div>
<div class="small-box"></div>

<style>
.large-box,
.small-box {
    margin: 16px;
    border-radius: 12px;
    background: #6c5ce7;
}

.large-box {
    width: 80px;
    height: 80px;
    animation: shared-pulse 2s ease-in-out infinite;
}

.small-box {
    width: 40px;
    height: 40px;
    animation: shared-pulse 4s ease-in-out infinite;
}

@keyframes shared-pulse {
    0%, 100% { transform: scale(1); }
    50% { transform: scale(1.15); }
}
</style>

Оба блока пульсируют, но маленький проходит одну итерацию вдвое дольше. Изменение кадра 50% повлияет сразу на оба элемента.

Несколько анимаций на одном элементе

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

Откройте этот пример в отдельном HTML-файле:

<div class="card">Карточка</div>

<style>
.card {
    width: 120px;
    height: 64px;
    display: grid;
    place-items: center;
    border-radius: 12px;
    color: white;
    background: #6c5ce7;
    animation:
        fade 2s ease-out forwards,
        card-slide 2s ease-in-out forwards;
}

@keyframes fade {
    from { opacity: 0; }
    to { opacity: 1; }
}

@keyframes card-slide {
    from { transform: translateX(0); }
    to { transform: translateX(120px); }
}
</style>

Карточка одновременно появляется и перемещается вправо на 120 пикселей. Через 2 секунды она остается видимой в конечной позиции.

Здесь fade меняет только прозрачность, а card-slide управляет перемещением. Если обе анимации меняют transform, значения по умолчанию не складываются: приоритет получает последняя анимация в списке.

Производительность CSS-анимаций

Плавность зависит от того, какую часть страницы браузер пересчитывает на каждом кадре. Начните со свойств, которые меняют отрисовку без перестройки геометрии.

  • Выбирайте transform и opacity. Они подходят для перемещения, масштаба, поворота и появления.
  • Осторожно меняйте top, left, width и height. Такие свойства могут запускать перерасчет расположения элементов.
  • Не ставьте will-change на все элементы. Свойство заранее подсказывает браузеру о предстоящем изменении, но лишние слои расходуют память.
  • Не используйте translate3d() как обязательный хак. Браузер сам выбирает подходящий способ ускорения, когда это уместно.
.button {
    transition: transform 180ms ease, opacity 180ms ease;
}

.button:hover {
    transform: translateY(-2px);
    opacity: 0.9;
}

Для короткого эффекта при наведении обычно хватит обычного transform.

Добавляйте will-change: transform, если диагностика показывает пользу для конкретного элемента. При временной анимации убирайте подсказку после завершения эффекта.

Управление анимацией из JavaScript

JavaScript может запустить, остановить или перезапустить CSS-анимацию. Браузер также отправляет события начала, повторения и завершения.

Создайте отдельный HTML-файл с этим примером. Скрипт стоит после блока и кнопок: к моменту поиска элементы уже существуют.

<div class="control-box"></div>
<button type="button" class="pause-button">Пауза</button>
<button type="button" class="restart-button">Сначала</button>
<p class="animation-status" aria-live="polite">Ожидание запуска</p>

<style>
.control-box {
    width: 64px;
    height: 64px;
    margin-bottom: 16px;
    border-radius: 12px;
    background: #6c5ce7;
}

.control-box.is-animated {
    animation: controlled-slide 2s ease-in-out 0s 3 alternate forwards;
}

@keyframes controlled-slide {
    from { transform: translateX(0); }
    to { transform: translateX(120px); }
}
</style>

<script>
const box = document.querySelector('.control-box');
const pauseButton = document.querySelector('.pause-button');
const restartButton = document.querySelector('.restart-button');
const status = document.querySelector('.animation-status');
let paused = false;

box.addEventListener('animationstart', () => {
    status.textContent = 'Анимация началась';
    console.log('animationstart');
});

box.addEventListener('animationiteration', () => {
    status.textContent = 'Началась следующая итерация';
    console.log('animationiteration');
});

box.addEventListener('animationend', () => {
    status.textContent = 'Анимация завершилась';
    pauseButton.disabled = true;
    console.log('animationend');
});

pauseButton.addEventListener('click', () => {
    paused = !paused;
    box.style.animationPlayState = paused ? 'paused' : 'running';
    pauseButton.textContent = paused ? 'Продолжить' : 'Пауза';
    status.textContent = paused ? 'Анимация на паузе' : 'Анимация продолжается';
});

function restartAnimation() {
    box.classList.remove('is-animated');
    void box.offsetWidth;
    paused = false;
    box.style.animationPlayState = 'running';
    pauseButton.textContent = 'Пауза';
    pauseButton.disabled = false;
    box.classList.add('is-animated');
}

restartButton.addEventListener('click', restartAnimation);
box.classList.add('is-animated');
</script>

Блок выполнит 3 прохода по 2 секунды: вперед, назад, вперед. После завершения он останется справа благодаря forwards.

Событие animationstart сообщает о начале активной части после задержки. Событие animationiteration возникает на границе повторов, кроме завершения последней итерации. Тогда браузер отправляет animationend. При одной итерации события animationiteration не будет.

В примере консоль покажет начало, две границы повторов и завершение.

Пауза из JavaScript

Во время движения нажмите «Пауза»: блок застынет на текущем кадре. Кнопка сменит надпись на «Продолжить». Следующее нажатие возобновит движение с места остановки.

Обработчик меняет animation-play-state через свойство animationPlayState.

После последнего прохода кнопка паузы станет недоступной. Завершенную анимацию запускает заново кнопка «Сначала».

Перезапуск CSS-анимации

Нажмите «Сначала» во время движения, паузы или после завершения. Блок вернется к началу пути и выполнит все 3 прохода.

Функция restartAnimation() удаляет класс, который назначает анимацию. Чтение offsetWidth заставляет браузер применить промежуточное состояние. Повторное добавление класса создает новую анимацию.

Повторное присваивание уже установленного класса анимацию не перезапускает.

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

Animation и transition: что выбрать

transition описывает переход между двумя состояниями, например обычной кнопкой и кнопкой при наведении. animation использует отдельные ключевые кадры и поддерживает повторения.

ЗадачаПодход
Плавно изменить кнопку при наведенииtransition
Показать несколько промежуточных состоянийanimation
Повторять движение без действия пользователяanimation
Плавно вернуть элемент после ухода указателяtransition

Выбирайте transition, когда событие меняет состояние элемента. Берите animation, когда сценарию нужны ключевые кадры или повторение. Оба механизма поддерживают задержку и события.

Доступность и уменьшение движения

Некоторым пользователям интенсивное движение мешает читать страницу или вызывает плохое самочувствие. Уважайте системную настройку prefers-reduced-motion и сокращайте декоративные эффекты.

@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;
    }
}

Такое правило резко сокращает длительность эффектов на всей странице. В сложном интерфейсе задавайте исключения для конкретных компонентов и сохраняйте понятное конечное состояние.

Не оставляйте движение единственным сигналом изменения состояния. Добавьте подпись, изменение цвета или другой понятный сигнал.

Совместимость и новые возможности

Актуальные браузеры поддерживают animation и @keyframes без вендорных префиксов. Для старых браузеров Autoprefixer может добавить нужные варианты во время сборки проекта.

Свойство animation-composition управляет сочетанием эффектов, которые меняют одно свойство. Свойство animation-timeline позволяет связать анимацию со шкалой прокрутки. Эти механизмы расширяют базовые примеры, но для первой анимации не нужны.

Как проверить и исправить первую анимацию

Если элемент не двигается, проверьте связь имени и кадров. Затем проверьте длительность и наличие свойства, которое меняется внутри @keyframes.

  1. Откройте инструменты разработчика и выберите анимируемый элемент.
  2. Найдите в стилях объявление animation и убедитесь, что имя совпадает с именем @keyframes.
  3. Проверьте, что длительность содержит единицу времени, например 2s.
  4. Временно замените infinite на 1, чтобы увидеть окончание анимации.
  5. Если движение дергается, замените позиционные свойства на transform и уберите лишний will-change.

После исправлений блок должен пройти путь между кадрами и сохранить конечное состояние, если вы указали forwards или both.

Чтобы вернуть исходный пример, восстановите animation: slide 2s ease-in-out infinite alternate. Удалите экспериментальные объявления подсвойств, которые стоят после него.

Совет от нашей редакции: сначала соберите эффект на двух кадрах, затем добавляйте задержку, повторение и управление из JavaScript. Так вы быстрее найдете причину ошибки.

Комментарии

Оставьте свой комментарий
Z
Zergo

Статья классная, спасибо!
есть вопрос,
а как сделать Паузу(к примеру в 5s) в конце анимации и чтобы эта пауза повторялась каждый цикл?

S
Santo

Спасибо огромное! Вы очень помогли!