$ sudo teach IT

Простая анимация «проиграться один раз за столько-то секунд» — только начало. В реальных интерфейсах нужен тонкий контроль: индикатор загрузки должен крутиться бесконечно, значок уведомления — пульсировать снова и снова, элементы в списке — появляться друг за другом с нарастающей задержкой, а анимация появления — оставлять элемент в конечном состоянии, а не сбрасывать назад. Всё это задают дополнительные свойства анимации: задержка, число повторов, направление и animation-fill-mode. Освоив их, вы превратите одноразовое движение в управляемый, повторяющийся и предсказуемый эффект.

После этого урока вы сможете точно управлять поведением анимации: сколько раз она повторяется, в какую сторону идёт, когда стартует и что остаётся на экране после её завершения.

Что уже умеем / что добавим

Вы умеете описывать кадры через @keyframes и запускать анимацию свойством animation с именем и длительностью. Теперь добавим управление: задержку старта, повторы, смену направления и фиксацию конечного состояния. В сквозном проекте мы заставим значок-метку на карточке пульсировать бесконечно и закрепим появление секции в конечном виде.

Что вы узнаете в рамках статьи

  • Как отложить старт анимации через animation-delay.
  • Как повторять анимацию, в том числе бесконечно, через animation-iteration-count.
  • Как менять направление проигрывания через animation-direction.
  • Как сохранять состояние до и после анимации через animation-fill-mode.

Ключевые термины

  • animation-delay — задержка перед началом анимации.
  • animation-iteration-count — сколько раз проигрывается анимация (число или infinite).
  • animation-direction — направление проигрывания (normal, reverse, alternate).
  • animation-fill-mode — какое состояние сохраняется до старта и после конца анимации.
  • infinite — значение для бесконечного повтора анимации.

Задержка старта: animation-delay

animation-delay откладывает начало анимации на заданное время. Это полезно, когда нужно, чтобы элемент появился не сразу, или чтобы несколько элементов возникали по очереди:

.item {
  animation: appear 0.5s ease-out;
}

.item:nth-child(2) {
  animation-delay: 0.1s;
}

.item:nth-child(3) {
  animation-delay: 0.2s;
}

Здесь первый элемент появляется сразу, второй — с задержкой 0,1 секунды, третий — 0,2 секунды. Получается эффект «каскада»: элементы возникают друг за другом, а не разом. Задержку задают в секундах или миллисекундах, как и длительность. Обратите внимание: пока идёт задержка, по умолчанию элемент показан в своём обычном виде — это иногда нужно скорректировать через fill-mode, о котором ниже.

Повторы: animation-iteration-count

По умолчанию анимация проигрывается один раз. Свойство animation-iteration-count задаёт, сколько раз её повторить. Можно указать число или ключевое слово infinite для бесконечного повтора:

@keyframes spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

.loader {
  animation: spin 1s linear infinite;
}

Индикатор загрузки будет крутиться бесконечно: анимация spin поворачивает элемент на полный круг за секунду и повторяется без остановки. Значение infinite — стандартный приём для спиннеров, пульсаций и любых непрерывных эффектов. Заметьте linear: для равномерного вращения подходит именно линейная функция тайминга, иначе оборот будет то ускоряться, то замедляться. Если нужно конечное число повторов, вместо infinite пишут число, например 3.

Направление: animation-direction

animation-direction управляет тем, в какую сторону проигрываются кадры. По умолчанию (normal) — от начала к концу. Значение reverse проигрывает наоборот, а особенно полезно alternate — оно чередует направление на каждом повторе:

@keyframes bob {
  from { transform: translateY(0); }
  to { transform: translateY(-8px); }
}

.dot {
  animation: bob 0.8s ease-in-out infinite alternate;
}

С alternate элемент плавно поднимается вверх (прямой проход), затем плавно опускается обратно (обратный проход) — и так бесконечно, создавая мягкое «покачивание». Без alternate он бы рывком возвращался в начало перед каждым повтором. Сочетание infinite alternate — частый приём для «дышащих», плавно колеблющихся элементов, где движение должно быть непрерывным и симметричным.

Состояние до и после: animation-fill-mode

По умолчанию после окончания анимации элемент возвращается к своему исходному стилю — как будто анимации не было. Это часто нежелательно: анимация появления должна оставить элемент видимым, а не снова спрятать. animation-fill-mode решает это:

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

.section {
  opacity: 0;
  animation: appear 0.6s ease-out forwards;
}

Значение forwards фиксирует конечный кадр: после анимации элемент остаётся в состоянии to (видимым), а не сбрасывается к opacity: 0. Есть и backwards — оно применяет начальный кадр ещё во время задержки (до старта), что удобно вместе с animation-delay, чтобы элемент не мелькал. Значение both объединяет оба поведения. Для анимаций появления forwards — почти обязательный параметр, иначе элемент после проигрывания «пропадёт» обратно.

Применяем в проекте

Оживим значок-метку «Хит» на карточке услуги — пусть мягко пульсирует, привлекая внимание, — и закрепим появление секции в конечном виде, чтобы она не пропадала после анимации.

@keyframes pulse {
  0% { transform: scale(1); }
  50% { transform: scale(1.12); }
  100% { transform: scale(1); }
}

.badge {
  animation: pulse 1.6s ease-in-out infinite;
}

.section {
  opacity: 0;
  animation: appear 0.6s ease-out forwards;
}

Обновите страницу: метка «Хит» плавно пульсирует без остановки, притягивая взгляд, а секция появляется один раз и остаётся видимой благодаря forwards. Мы задали бесконечный повтор для метки и зафиксировали конечное состояние для секции — движение стало именно таким, как задумано. Теперь анимации не просто проигрываются, а ведут себя предсказуемо и осмысленно.

Частые ошибки

  • Забыть forwards для появления.
    Без animation-fill-mode: forwards элемент после анимации вернётся к исходному стилю и «пропадёт».
  • infinite для эффекта появления.
    Появление должно играть один раз; бесконечный повтор уместен для спиннеров и пульсаций, а не для входа.
  • Возврат рывком без alternate.
    Для непрерывного «туда-обратно» нужен alternate, иначе элемент будет прыгать в начало перед каждым повтором.
  • Мелькание при задержке.
    Если анимация появления стартует с задержкой, добавьте backwards (или both), чтобы элемент не был виден до старта.

Итоги

Прекрасная работа — теперь вы управляете анимацией по-настоящему! Вы научились откладывать старт через animation-delay, повторять движение (в том числе бесконечно) с animation-iteration-count, менять направление через animation-direction и alternate, а также фиксировать состояние до и после анимации с помощью animation-fill-mode. В сквозном проекте метка на карточке ожила пульсацией, а секция стала появляться ровно один раз и оставаться на месте.

Отличный прогресс — вашими анимациями теперь можно тонко дирижировать! В следующем уроке мы разберём трансформации transform: научимся двигать, масштабировать, вращать и наклонять элементы — именно они делают анимации плавными и производительными.

На следующий урок