Урок 42. Теория: Анимации и переходы: transition и @keyframes

📁 Раздел: Современный CSS ⏱️ Время изучения: ~80 мин 🎯 Сложность: Средняя
#transition #transition-property #transition-duration #transition-timing-function #transition-delay #cubic-bezier #steps #keyframes #animation #animation-name #animation-duration #animation-timing-function #animation-iteration-count #animation-fill-mode #animation-delay #animation-direction #transform #opacity #media

⚡ Кратко

`transition` плавно меняет свойства между состояниями, а `@keyframes` описывает самостоятельную анимацию. Разбираем duration, easing, iteration, fill-mode, производительные `transform`/`opacity` и `prefers-reduced-motion`.

Главная идея

Движение в интерфейсе делает изменения понятными: вместо резкого скачка пользователь видит, как элемент перешёл из одного состояния в другое. В CSS есть два инструмента для этого. transition плавно «доводит» свойство от старого значения к новому, когда что-то меняется (наведение, фокус, добавленный класс). @keyframes + animation описывают самостоятельную анимацию по кадрам, которая запускается сама и может повторяться.

transition — это доводчик двери. Без него дверь хлопает: состояние мгновенно перескакивает с «открыто» на «закрыто». Доводчик не меняет начало и конец — он лишь делает путь между ними плавным. Точно так же transition не задаёт новых значений, а сглаживает переход к тем, что уже описаны в :hover или другом состоянии.

@keyframes — это раскадровка мультфильма. Аниматор рисует ключевые кадры: 0% — герой слева, 50% — в центре, 100% — справа. Браузер, как и студия, дорисовывает промежуточные фазы сам. Анимация живёт по своему сценарию и не ждёт действия пользователя.

Приём этого урока: переход и анимацию по-настоящему видно только «в движении» или при взаимодействии. Чтобы показать результат статично, ниже и в примерах мы используем раскадровку — несколько блоков «кадр 0% → 50% → 100%» рядом. Это те самые фазы, которые браузер проигрывает за доли секунды.

Как работает transition

У перехода четыре подсвойства. Запомнить их проще по вопросам: что, как долго, по какой кривой, с какой задержкой.

  • transition-propertyчто анимировать (transform, opacity, background-color… или all).
  • transition-durationкак долго идёт переход (180ms, 0.3s). Без неё перехода нет — длительность по умолчанию 0s.
  • transition-timing-functionпо какой кривой меняется скорость (ease, linear, ease-in-out, cubic-bezier()).
  • transition-delayзадержка перед стартом.

Раскадровка перехода ширины кнопки от обычного состояния к наведённому (кривая ease):

0% старт
50%
100% :hover
Один переход = непрерывная последовательность таких промежуточных кадров.

Кривые скорости (easing)

Easing задаёт, как распределяется скорость во времени. linear — равномерно (выглядит механически). ease (по умолчанию) — быстрый старт замедляется к концу. ease-in-out — плавно стартует и плавно тормозит, самый «живой» для UI. cubic-bezier() — своя кривая.

0 время 100% linear ease-in-out
Прямая (linear) — постоянная скорость; S-кривая (ease-in-out) — медленный старт и торможение.

Как работает @keyframes

Сначала описываем сценарий правилом @keyframes имя { … } с кадрами через from/to или проценты 0%…100%. Затем «вешаем» его на элемент свойством animation (имя, длительность, кривая, число повторов animation-iteration-count, поведение до/после animation-fill-mode). Раскадровка фейда-вверх:

блок
0%
блок
50%
блок
100%
@keyframes fade-up: прозрачность 0 → 1 и сдвиг снизу вверх.

Когда что применять

  • transition — для реакции на действие: hover/focus кнопок и карточек, раскрытие меню, смена класса.
  • @keyframes — для самостоятельных эффектов: появление при загрузке, пульс, спиннер, бесконечный цикл.
  • Анимируйте только transform и opacity — они дёшевы для браузера. width, height, top, left вызывают перерасчёт раскладки и тормозят.

Доступность: prefers-reduced-motion

Часть людей отключает анимации в системе (укачивание, вестибулярные нарушения). Уважайте их выбор через медиазапрос prefers-reduced-motion: reduce — это та же тема доступности, что в уроке 38.

Частая ошибка

Самая частая — забыть transition-duration (или поставить переход в :hover, а не на базовом селекторе). Тогда переход срабатывает только в одну сторону или не работает вовсе.

Базовый CSS урока

📄 lesson-42/styles.css
/* transition объявляем на БАЗОВОМ селекторе, чтобы переход шёл в обе стороны */
.feature-card,
.button {
  transition-property: transform, opacity, background-color;
  transition-duration: 180ms;
  transition-timing-function: ease;
}

.feature-card:hover,
.feature-card:focus-within {
  transform: translateY(-0.25rem);   /* :hover задаёт ЦЕЛЬ, transition её сглаживает */
}

/* самостоятельная анимация появления через @keyframes */
.feature-card {
  animation-name: fade-up;
  animation-duration: 500ms;
  animation-timing-function: ease-out;
  animation-fill-mode: both;          /* удержать стиль до старта и после финиша */
  animation-iteration-count: 1;
}

@keyframes fade-up {
  from { opacity: 0; transform: translateY(1rem); }
  to   { opacity: 1; transform: translateY(0); }
}

/* доступность: уменьшаем движение по системной настройке */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms;
    animation-iteration-count: 1;
    transition-duration: 0.01ms;
  }
}

Навигация