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

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

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

Чтобы движение выглядело гладким, браузер должен успевать рисовать кадр за ~16 мс (60 кадров в секунду). Если на кадр приходится слишком много работы, кадры «проседают» — анимация дёргается (это называют jank). Главный рычаг борьбы с этим — выбор анимируемого свойства.

Конвейер рендеринга: три этапа

Каждый кадр браузер потенциально проходит три дорожающих этапа:

  1. Layout (раскладка) — вычисление геометрии: где и какого размера каждый элемент. Меняется при анимации width, height, top, margin, padding.
  2. Paint (отрисовка) — заливка пикселей: цвета, тени, границы. Меняется при анимации background-color, box-shadow, color.
  3. Composite (композиция) — склейка готовых слоёв на экране. Сюда попадают transform и opacity.

Чем раньше этап, тем он дороже и тем больше тянет за собой: изменение Layout вынуждает заново сделать Paint и Composite. А анимация на этапе Composite не трогает первые два — поэтому она самая дешёвая.

Дёшево: transform и opacity

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

Дорогой способДешёвая замена
left / toptransform: translate(...)
width / heighttransform: scale(...)
visibility + перерисовкаopacity
/* Дорого: каждый кадр — пересчёт раскладки */
.bad {
  transition: left 0.3s, width 0.3s;
}
/* Дёшево: только композиция */
.good {
  transition: transform 0.3s, opacity 0.3s;
}

Дорого: layout-свойства

Анимация размеров и позиции через layout-свойства запускает пересчёт раскладки на каждом кадре. Для одного маленького блока это незаметно, но если внутри много содержимого или элементов на странице сотни, кадры проседают. Особенно коварна анимация box-shadow: она вызывает дорогой повторный Paint. Часто тень имитируют отдельным псевдоэлементом, у которого анимируют opacity.

will-change: подсказка браузеру

Свойство will-change заранее сообщает браузеру, что элемент скоро будет меняться, — и тот может вынести его на отдельный композиторный слой до начала анимации, избежав рывка на первом кадре.

.card {
  transition: transform 0.2s;
}
.list:hover .card {
  will-change: transform;
  transform: translateY(-4px);
}

Но will-change — не «кнопка ускорения». Каждый слой стоит памяти; если повесить его на десятки элементов навсегда, станет хуже, а не лучше. Правила: применяйте точечно, в идеале незадолго до анимации (например, по :hover родителя), и не оставляйте на постоянной основе на множестве элементов.

Слои композитора

Браузер может вынести элемент на собственный слой (как прозрачную плёнку поверх остальной страницы). Анимация transform/opacity такого слоя сводится к сдвигу и смешиванию готовой картинки — это умеет делать GPU очень быстро. На слой элемент попадает по разным причинам: анимация transform/opacity, will-change, иногда position: fixed или видео. В панели Layers/Rendering браузерных DevTools слои видно, и там же можно проверить, не плодите ли вы их слишком много.

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

Для части людей анимации — не украшение, а проблема: интенсивное движение, параллаксы и зумы могут вызывать головокружение, тошноту и приступы у людей с вестибулярными расстройствами. В системных настройках есть переключатель «уменьшить движение», и его значение доступно в CSS через медиазапрос. Уважать его — обязательная часть доступного интерфейса.

.banner {
  animation: slide-in 0.5s ease-out;
}
@media (prefers-reduced-motion: reduce) {
  .banner {
    animation: none; /* показать сразу, без движения */
  }
  * {
    transition-duration: 0.01ms !important;
  }
}

Хороший тон — не выключать интерфейс полностью, а заменять крупные перемещения на мгновенное появление или лёгкое затухание opacity: смысл анимации (что-то изменилось) сохраняется, а дискомфортное движение убирается.

Как это измерить

Не угадывайте «на глаз». В DevTools есть вкладка Performance: запишите анимацию и посмотрите на полосу FPS и на длинные задачи главного потока — частые жёлтые блоки Layout/Recalculate Style выдают дорогую анимацию. Панель Rendering умеет включить счётчик FPS и подсветку перерисовываемых областей (Paint flashing): если при движении мигает половина экрана, вы анимируете не то свойство. Эти инструменты быстро показывают, действительно ли ваша анимация осталась на этапе композиции.

Как это работает под капотом

На каждый кадр главный поток браузера решает, какие этапы конвейера нужно повторить из-за изменившихся свойств. Если изменился только transform или opacity элемента, лежащего на своём слое, главный поток почти не задействован — кадр собирает композитор (часто на GPU), и анимация остаётся плавной, даже если JavaScript занят. Если же изменилось layout-свойство, главный поток обязан заново посчитать раскладку всей затронутой части дерева, потом Paint, потом Composite — и при нехватке времени кадр пропускается.

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

  • Анимация top/left/width вместо transform. Первый же кандидат на jank.
  • will-change на всех элементах подряд и навсегда. Лишние слои съедают память и замедляют страницу.
  • Анимация box-shadow в цикле. Дорогой повторный Paint; имитируйте тень через opacity псевдоэлемента.
  • Игнорирование prefers-reduced-motion. Интерфейс становится недоступным для части пользователей.

Итоги

  • Кадр проходит этапы Layout → Paint → Composite; ранние этапы дороже.
  • Дёшево анимировать transform и opacity — они живут на этапе композиции.
  • Layout-свойства (width, top, margin) и box-shadow в анимации дороги.
  • will-change готовит слой заранее, но применяйте его точечно.
  • prefers-reduced-motion — обязательная забота о доступности: уменьшайте движение по запросу.
Проверьте себя
1. Какие два свойства браузер анимирует дешевле всего, на этапе композиции?
Awidth и height
Btransform и opacity
Ctop и left
Dbox-shadow и color
2. Почему анимация свойства width считается дорогой?
Awidth нельзя анимировать вообще
BОна запускает пересчёт раскладки (layout) на каждом кадре, тянущий за собой paint и composite
CОна работает только в Safari
DОна требует обязательного will-change
3. Как правильно использовать will-change?
AПовесить на все элементы страницы навсегда для максимальной скорости
BПрименять точечно и незадолго до анимации, не оставляя постоянно на множестве элементов
CИспользовать только вместе с display: none
DСтавить его на body один раз
4. Что описывает медиазапрос prefers-reduced-motion: reduce?
AЧто у пользователя медленный интернет
BЧто пользователь включил в системе режим уменьшения движения, и анимации стоит ослабить или убрать
CЧто экран маленького размера
DЧто браузер не поддерживает анимации