Уменьшенное движение и производительность
Что это такое
Любую выпускаемую анимацию ограничивают две вещи. Уменьшенное движение: операционная система даёт настройку для людей, которым движение причиняет недомогание или отвлекает их, и prefers-reduced-motion сообщает о ней. Производительность: каждая анимация должна уложиться в бюджет кадра браузера, иначе она заикается и заставляет страницу казаться сломанной.
Почему это важно
Движение может вызывать тошноту, головокружение и мигрень у людей с вестибулярными расстройствами, а настройка, которой они об этом говорят, — та самая, которую обязана уважать ваша таблица стилей. Игнорировать её — не вопрос вкуса: это и отказ по стандарту, и реальный вред живому пользователю. Производительность не менее практична: если анимировать свойство, которое браузер вынужден раскладывать заново, на телефоне среднего класса появятся рывки, даже когда на машине, где вы писали код, всё плавно, а во время движения рывки куда заметнее, чем при статичной перерисовке.
Ключевые идеи
Уважайте настройку уменьшенного движения
Оберните необязательное движение в запрос no-preference, чтобы умолчанием оставался статичный, неподвижный интерфейс. Затем решите, что сохранить: изменения прозрачности и цвета обычно безопасны и часто нужны, чтобы показать появление элемента, а параллакс, крупные сдвиги и зацикленная фоновая анимация должны прекратиться.
@media (prefers-reduced-motion: no-preference) {
.drawer { transition: transform 160ms ease-out; }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
Общее правило — страховка для сторонних виджетов, и оно должно лежать под продуманной реализацией, а не заменять её.
Анимируйте трансформации и прозрачность
Изменение transform или opacity браузер умеет компоновать, не пересчитывая раскладку и не перерисовывая, поэтому дешёвыми считаются именно эти два свойства. Анимация width, height, top, left или margin запускает раскладку на каждом кадре, а анимация цветов — перерисовку. Если свойство раскладки всё же должно меняться, анимируйте трансформацию, которая даёт тот же визуальный результат.
/* дешёво: композитинг */
.row-enter { transform: translateY(0); opacity: 1; }
/* дорого: раскладка на каждом кадре */
.row-enter { height: 2.5rem; margin-top: 0; }
Уводите работу с главного потока, где возможно
Переход или анимация CSS на композитном свойстве идут независимо от JavaScript, поэтому занятый главный поток их не останавливает. Значит, переход CSS или Web Animations предпочтительнее цикла на requestAnimationFrame, который пересчитывает позиции каждый кадр.
Не боритесь с бюджетом кадра
При типичной частоте обновления на кадр приходится примерно 16 миллисекунд, и их делят с ним всё остальное, чем занята страница. Анимация, запущенная посреди долгой задачи, уронит кадры именно в тот момент, когда пользователь смотрит. Держите анимации короткими, запускайте их после дорогой работы и не читайте свойства раскладки внутри покадрового колбэка.
Проверяйте с включённой настройкой и с зажатым CPU
Включите настройку операционной системы и убедитесь, что интерфейс по-прежнему работоспособен и понятен. Затем зажмите CPU в инструментах производительности браузера и посмотрите анимацию снова. Достаточно сделать это один раз, и проверка поймает оба класса отказов раньше пользователя.
Практика
Спрячьте одну анимацию за настройкой
Возьмите написанный вами переход и перенесите его за prefers-reduced-motion: no-preference, а затем включите настройку и пройдите тот же сценарий.
Готово, когда: сценарий остаётся понятным без движения, и ничто не зависит от завершения анимации для работоспособности.
Замените анимацию раскладки композитной
Найдите анимацию, которая меняет height или margin на каждом кадре, и перепишите её на transform и opacity, убедившись, что визуальный результат тот же.
Готово, когда: запись производительности не показывает работы раскладки во время анимации, а воспринимаемое движение не изменилось.
Частые ошибки
- Игнорирование
prefers-reduced-motion— часть пользователей страдает от анимации и не может от неё отказаться; исправить: прячьте движение заno-preference. - Анимация
heightилиmargin— каждый кадр вынуждает проход раскладки, и на медленном железе анимация заикается; исправить: анимируйтеtransformиopacity. - JavaScript для покадрового цикла там, где хватило бы CSS — анимация конкурирует с работой приложения за главный поток; исправить: предпочитайте переход CSS или Web Animations API.
- Отключение всей обратной связи при уменьшенном движении — интерфейс становится двусмысленным насчёт того, случилось ли что-нибудь; исправить: оставьте мгновенные смены состояния или только через прозрачность.
- Проверка только на быстрой машине — рывки проявляются на железе среднего класса, которого разработчик не видит; исправить: зажимайте CPU при проверке анимации.
Что читать дальше
- Осмысленное движение (02-frontend/16-motion-design/01-purposeful-motion) — выбор того, что анимировать, который идёт до того, как решено, как это реализовать.
- Доступность (WCAG / a11y) (02-frontend/11-accessibility-wcag-a11y) — стандарт, из которого эти требования происходят.
- Веб-производительность (Core Web Vitals) (02-frontend/26-web-performance-core-web-vitals) — как анимация и долгие задачи проявляются в метриках.