Чем отличаются transition и animation в CSS?

MiddleCSS #css #animation

Вопрос

Чем transition отличается от @keyframes/animation, и какие свойства выгоднее анимировать с точки зрения производительности?

Короткий ответ

transition описывает плавный переход между двумя состояниями свойства и запускается только при изменении значения (например, по :hover или смене класса). @keyframes/animation описывает многошаговую последовательность состояний, которая может запускаться сама по себе, зацикливаться и повторяться без внешнего триггера. С точки зрения производительности выгоднее анимировать transform и opacity, а не свойства, вызывающие layout/paint.

Подробный ответ

transition реагирует на изменение значения CSS-свойства: было одно значение, стало другое — браузер плавно интерполирует между ними за заданное время. У transition всегда ровно две точки — начало и конец, без промежуточных шагов. Требует внешнего триггера: изменения класса, псевдокласса (:hover, :focus), инлайн-стиля через JS.

@keyframes + animation позволяют описать произвольное число промежуточных состояний (0%, 50%, 100% и т.д.) и не нуждаются во внешнем триггере — анимация может стартовать сама при применении класса и работать автономно. Дополнительные возможности, которых нет у transition:

  • animation-iteration-count — зацикливание (infinite или конкретное число повторов).
  • animation-direction — alternate для движения туда-обратно.
  • animation-fill-mode — сохранение состояния до/после анимации (forwards, backwards).
  • animation-delay, animation-play-state — управление паузой и задержкой.

Производительность: браузер рендерит страницу через конвейер layout → paint → composite. Анимация свойств, влияющих на геометрию (width, height, top, left, margin), заставляет браузер каждый кадр пересчитывать layout всей затронутой части дерева — это дорого и может привести к "дёрганой" анимации (jank). Анимация transform (translate, scale, rotate) и opacity может выполняться только на этапе composite, часто на GPU, минуя layout и paint — это дешевле и плавнее. will-change: transform можно использовать как подсказку браузеру заранее подготовить отдельный композитный слой, но злоупотреблять им не стоит — лишние слои увеличивают потребление памяти.

Пример

/* transition: переход между состояниями по hover */
.button {
  background-color: #3b82f6;
  transform: translateY(0);
  transition: background-color 0.2s ease, transform 0.2s ease;
}
.button:hover {
  background-color: #2563eb;
  transform: translateY(-2px);
}

/* animation: автономная зацикленная последовательность */
@keyframes pulse {
  0%   { transform: scale(1);   opacity: 1; }
  50%  { transform: scale(1.05); opacity: 0.8; }
  100% { transform: scale(1);   opacity: 1; }
}

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

/* плохо для производительности: анимируется layout-свойство */
.bad-example {
  transition: width 0.3s ease, left 0.3s ease;
}

/* хорошо: тот же визуальный эффект через transform */
.good-example {
  transition: transform 0.3s ease;
}

Дополнительные вопросы

  • Что такое composite layer и почему transform/opacity могут анимироваться в обход layout и paint?
  • Как работает animation-fill-mode: forwards и зачем он нужен?
  • Чем requestAnimationFrame в JS отличается от CSS-анимаций с точки зрения контроля и производительности?