На пути к показателю плавности анимации

Узнайте об измерении анимации, о том, как думать о кадрах анимации и об общей гладкости страницы.

Бехдад Бахшинатех
Behdad Bakhshinategh
Джонатан Росс
Jonathan Ross

Вы, вероятно, сталкивались со страницами, которые «тормозят» или «зависают» при прокрутке или анимации. Мы любим говорить, что такие ситуации не всегда бывают плавными . Чтобы решить подобные проблемы, команда Chrome работает над расширением поддержки наших лабораторных инструментов для обнаружения анимации, а также постоянно совершенствует диагностику конвейера рендеринга в Chromium.

Мы хотели бы поделиться последними достижениями, дать конкретные рекомендации по использованию инструментов и обсудить идеи для будущих метрик плавности анимации. Как всегда, мы будем рады вашим отзывам .

В этом посте будут рассмотрены три основные темы:

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

Что такое анимация?

Анимация оживляет контент! Заставляя контент двигаться, особенно в ответ на действия пользователя, анимация может сделать взаимодействие с пользователем более естественным, понятным и увлекательным.

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

Как работает анимация?

Коротко говоря, конвейер рендеринга состоит из нескольких последовательных этапов:

  1. Стиль: вычислите стили, применяемые к элементам.
  2. Макет: создайте геометрию и положение каждого элемента.
  3. Раскрашиваем: заполняем слои пикселями для каждого элемента.
  4. Композитный: Вывод слоев на экран.

Хотя существует множество способов определения анимации, все они по сути работают по одному из следующих принципов:

  • Настройка свойств макета .
  • Регулировка свойств краски .
  • Настройка свойств композита .

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

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

Определение декларативной CSS-анимации или использование веб-анимации , а также анимация составных свойств — отличный первый шаг к обеспечению плавной и эффективной анимации. Однако это само по себе не гарантирует плавности, поскольку даже у эффективной веб-анимации есть ограничения по производительности. Именно поэтому всегда важно измерять!

Что такое кадры анимации?

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

Дисплеи обновляются с определённым интервалом, поэтому визуальные обновления выполняются пакетно. Многие дисплеи обновляются с фиксированным интервалом, например, 60 раз в секунду (то есть 60 Гц). Некоторые современные дисплеи могут предлагать более высокую частоту обновления (становится всё более распространённой частота 90–120 Гц). Часто такие дисплеи могут автоматически адаптироваться к нужной частоте обновления или даже полностью изменять частоту кадров.

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

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

На самом деле это случается часто! Это не всегда заметно, особенно для статичного или документоподобного контента, что часто встречается на веб-платформах. Пропущенные кадры становятся заметны только при важных визуальных обновлениях, например, при анимации, для которой требуется постоянный поток обновлений анимации для обеспечения плавности движения.

Что влияет на кадры анимации?

Веб-разработчики могут существенно повлиять на способность браузера быстро и эффективно отображать и представлять визуальные обновления!

Вот несколько примеров:

  • Использование контента, который слишком велик или требует слишком много ресурсов для быстрого декодирования на целевом устройстве.
  • Использование слишком большого количества слоев требует слишком много памяти графического процессора.
  • Определение слишком сложных стилей CSS или веб-анимации.
  • Использование антишаблонов проектирования, которые мешают быстрой оптимизации рендеринга.
  • Слишком много работы JS в основном потоке, что приводит к длительным задачам, блокирующим визуальные обновления.

Но как узнать, что кадр анимации не достиг своего крайнего срока и стал причиной пропуска кадра?

Один из возможных методов — использование опроса requestAnimationFrame() , однако он имеет ряд недостатков. requestAnimationFrame() , или «rAF», сообщает браузеру о желании выполнить анимацию и запрашивает возможность сделать это до следующего этапа отрисовки конвейера рендеринга. Если ваша функция обратного вызова не вызывается в ожидаемое время, это означает, что отрисовка не была выполнена, и один или несколько кадров были пропущены. Опрашивая и подсчитывая частоту вызовов rAF, можно вычислить своего рода метрику «кадров в секунду» (FPS).

let frameTimes = [];
function pollFramesPerSecond(now) {
  frameTimes = [...frameTimes.filter(t => t > now - 1000), now];
  requestAnimationFrame(pollFramesPerSecond);
  console.log('Frames per second:', frameTimes.length);
}
requestAnimationFrame(pollFramesPerSecond);

Использование опроса requestAnimationFrame() не является хорошей идеей по нескольким причинам:

  • Каждый скрипт должен настроить свой собственный цикл опроса.
  • Это может заблокировать критический путь.
  • Даже если опрос rAF выполняется быстро, это может помешать requestIdleCallback() планировать длинные блоки простоя при постоянном использовании (блоки, превышающие один кадр).
  • Аналогичным образом, отсутствие длительных бездействующих блоков не позволяет браузеру планировать другие длительные задачи (например, более длительную сборку мусора и другую фоновую или спекулятивную работу).
  • Если опрос включается и выключается, то вы пропустите случаи превышения бюджета кадра.
  • Опрос будет сообщать о ложноположительных результатах в случаях, когда браузер использует переменную частоту обновления (например, из-за состояния питания или видимости).
  • И что самое важное, он на самом деле не фиксирует все типы обновлений анимации!

Слишком большая нагрузка на основной поток может повлиять на возможность просмотра кадров анимации. Взгляните на пример Jank , чтобы увидеть, как анимация, управляемая rAF, при слишком большой нагрузке на основной поток (например, при компоновке) приводит к пропуску кадров, уменьшению количества обратных вызовов rAF и снижению FPS.

Когда основной поток событий застревает, визуальные обновления начинают тормозить. Это ужасно!

Многие инструменты измерения уделяют большое внимание способности основного потока своевременно завершать работу и плавности кадров анимации. Но это ещё не всё! Рассмотрим следующий пример:

На видео выше показана страница, которая периодически внедряет длинные задачи в основной поток. Эти длинные задачи полностью сводят на нет возможность страницы предоставлять определённые типы визуальных обновлений, и в левом верхнем углу видно соответствующее падение FPS, сообщаемого requestAnimationFrame() , до 0.

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

Это пример, в котором одновременно присутствует множество пропущенных кадров в основном потоке, но при этом в потоке компоновщика всё ещё присутствует множество успешно доставленных кадров прокрутки. После завершения долгой задачи обновление отрисовки в основном потоке не приводит к визуальным изменениям. Опрос rAF показал падение количества кадров до 0, но визуально пользователь не заметит разницы!

С кадрами анимации все не так просто.

Кадры анимации: обновления, которые имеют значение

Приведенный выше пример показывает, что речь идет не только о requestAnimationFrame() .

Итак, когда обновления анимации и кадры анимации имеют значение? Вот несколько критериев, которые мы обдумываем и по которым хотели бы получить обратную связь:

  • Обновления основного и компоновочного потоков
  • Отсутствуют обновления покраски
  • Обнаружение анимации
  • Качество против количества

Обновления основного и компоновочного потоков

Обновления кадров анимации не являются логическими. Кадры могут быть либо полностью удалены, либо полностью представлены. Существует множество причин, по которым кадр анимации может быть представлен частично . Другими словами, он может одновременно содержать устаревший контент и отображать новые визуальные обновления .

Самый распространенный пример — когда браузер не может произвести новое обновление основного потока в течение крайнего срока кадра, но у него есть новое обновление потока компоновщика (например, пример с потоковой прокруткой, приведенный ранее).

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

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

В общем случае мы считаем кадры, содержащие лишь некоторые новые визуальные обновления, но не все из них, частичным кадром . Частичные кадры встречаются довольно часто. В идеале частичные обновления должны включать как минимум наиболее важные визуальные обновления, такие как анимация, но это возможно только в том случае, если анимация управляется потоком компоновщика.

Отсутствуют обновления покраски

Другой тип частичного обновления возникает, когда медиа-данные, такие как изображения, не были полностью декодированы и растеризованы к моменту покадрового представления.

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

С каждым кадром, отображённым в процессе рендеринга, можно отслеживать, сколько последних визуальных обновлений действительно попало на экран. Измерение способности делать это в течение нескольких кадров (или времени) широко известно как пропускная способность кадров .

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

Однако не все типы низкой частоты кадров плохи. Если страница практически неактивна и нет активной анимации, низкая частота кадров так же визуально привлекательна, как и высокая (и, к тому же, может экономить заряд батареи!).

Так когда же пропускная способность кадра имеет значение?

Обнаружение анимации

Высокая пропускная способность особенно важна в периоды с важной анимацией. Различные типы анимации зависят от визуальных обновлений из определённого потока (основного, компоновщика или рабочего), поэтому их визуальное обновление зависит от того, будет ли этот поток обновляться в установленные сроки. Мы говорим, что данный поток влияет на плавность, когда есть активная анимация, зависящая от обновления этого потока.

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

Даже при использовании requestAnimationFrame() нельзя всегда предполагать, что каждый вызов rAF обязательно приводит к визуальному обновлению или анимации. Например, использование опроса rAF только для отслеживания частоты кадров (как показано выше) само по себе не должно влиять на показатели плавности, поскольку визуальное обновление отсутствует.

Качество против количества

Наконец, обнаружение анимации и обновлений кадров анимации — это только часть дела, поскольку оно фиксирует только количество обновлений анимации, а не качество.

Например, при просмотре видео вы можете наблюдать стабильную частоту кадров 60 кадров в секунду. Технически это идеально плавно, но само видео может иметь низкий битрейт или проблемы с сетевой буферизацией. Это не отражается напрямую в метриках плавности анимации, но всё равно может раздражать пользователя.

Или игра, которая использует <canvas> (возможно, даже с использованием таких технологий, как внеэкранный холст для обеспечения постоянной частоты кадров), может технически быть идеально гладкой с точки зрения кадров анимации, но при этом не сможет загружать высококачественные игровые ресурсы в сцену или будет демонстрировать артефакты рендеринга.

И, конечно же, на сайте могут быть просто ужасные анимации 🙂

Старая школа в стадии строительства GIF

Я имею в виду, что для своего времени они были довольно круты!

Состояния одного кадра анимации

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

Вот спектр способов, с помощью которых мы интерпретируем состояние отдельного кадра анимации, упорядоченный от лучшего к худшему случаю:

Обновление не требуется Время простоя, повтор предыдущего кадра.
Полностью представлено Обновление основного потока было либо завершено в установленные сроки, либо обновление основного потока не требовалось.
Частично представлено Только Компоновщик; отложенное обновление основного потока не имело визуальных изменений.
Частично представлено Только Компоновщик; основной поток получил визуальное обновление, но это обновление не включало анимацию, влияющую на плавность.
Частично представлено Только для композитора; в основном потоке произошло визуальное обновление, повлиявшее на плавность, но вместо него появился и использовался ранее устаревший кадр.
Частично представлено Только Композитор; без желаемого основного обновления, а обновление Композитора имеет анимацию, которая влияет на плавность.
Частично представлено Только Композитор, но обновление Композитора не имеет анимации, которая влияет на плавность.
Выпавшая рама Обновления нет. Обновление композитора не требовалось, и основной этап был отложен.
Выпавшая рама Хотелось бы обновить композитора, но это было отложено.
Устаревший кадр Обновление было необходимо, оно было выполнено рендерером, но графический процессор все еще не представил его до истечения срока вертикальной синхронизации.

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

Собираем всё вместе: метрика процента пропущенных кадров

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

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

Ментальная модель должна двигаться от:

  1. Кадров в секунду , до
  2. Обнаружение отсутствующих и важных обновлений анимации, чтобы
  3. Процент падения за определенный период времени.

Важна доля времени, затраченного на ожидание важных обновлений . Мы считаем, что это соответствует тому, как пользователи на практике воспринимают плавность веб-контента. До сих пор мы использовали следующие показатели в качестве исходного набора:

  • Средний процент пропущенных кадров: для всех не-простых кадров анимации на всей временной шкале
  • Худший случай процента выпавших кадров: измеряется в течение скользящих промежутков времени длительностью в 1 секунду.
  • 95-й процентиль процента пропущенных кадров: измеряется за скользящие промежутки времени длительностью в 1 секунду.

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

Попробуйте сами в инструментах разработчика!

Производительность HUD

В Chromium есть удобный HUD-дисплей производительности, скрытый за флагом ( chrome://flags/#show-performance-metrics-hud ). В нём можно найти актуальные оценки таких показателей, как Core Web Vitals, а также несколько экспериментальных определений плавности анимации, основанных на проценте пропущенных кадров с течением времени.

Производительность HUD

Статистика рендеринга кадров

Включите «Статистика рендеринга кадров» в DevTools в настройках рендеринга, чтобы видеть новые кадры анимации в режиме реального времени. Они имеют цветовую кодировку, позволяющую отличать частичные обновления от полностью отсутствующих. Указанная частота кадров в секунду (fps) относится только к полностью отображённым кадрам.

Статистика рендеринга кадров

Просмотр кадров в записях профилей производительности DevTools

Панель «Производительность» в DevTools уже давно включает в себя просмотрщик кадров . Однако он несколько отстал от того, как работает современный конвейер рендеринга. В последнее время было внесено множество улучшений, даже в последней версии Chrome Canary, которые, по нашему мнению, значительно упростят отладку проблем с анимацией.

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

Средство просмотра кадров в Chrome DevTools

трассировка Chrome

Наконец, с помощью Chrome Tracing, инструмента для глубокого анализа деталей, вы можете записать трассировку рендеринга веб-контента через новый пользовательский интерфейс Perfetto (или about:tracing ) и подробно изучить графический конвейер Chrome. Это может быть непростой задачей, но в Chromium недавно было добавлено несколько функций, упрощающих задачу. Обзор доступных возможностей можно найти в документе «Жизнь фрейма» .

С помощью трассировочных событий можно однозначно определить:

  • Какие анимации запущены (с использованием событий TrackerValidation ).
  • Получение точной временной шкалы кадров анимации (с использованием событий с именем PipelineReporter ).
  • В случае нестабильных обновлений анимации выясните, что именно мешает ей работать быстрее (используя анализ событий в событиях PipelineReporter ).
  • Для анимации, управляемой вводом, посмотрите, сколько времени требуется для визуального обновления (с помощью событий, называемых EventLatency ).

Репортер конвейера Chrome Tracing

Что дальше?

Инициатива Web Vitals направлена на предоставление метрик и рекомендаций для создания отличного пользовательского опыта в интернете. Лабораторные метрики, такие как общее время блокировки (TBT), крайне важны для выявления и диагностики потенциальных проблем с интерактивностью. Мы планируем разработать аналогичную лабораторную метрику для оценки плавности анимации.

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

В будущем мы также хотели бы разработать API, которые позволят эффективно измерять плавность анимации как для реальных пользователей, так и в реальных условиях . Следите за обновлениями!

Обратная связь

Мы в восторге от всех недавних улучшений и инструментов разработчика, которые появились в Chrome для измерения плавности анимации. Попробуйте эти инструменты, оцените качество своих анимаций и расскажите нам, к чему это приведёт!

Вы можете отправить свои комментарии в группу Google web-vitals-feedback, указав в теме письма «[Smoothness Metrics]». Мы с нетерпением ждём вашего мнения!