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

Узнайте об измерении анимации, о том, как учитывать кадры анимации и общую плавность страницы.

Бехдад Бахшинатех
Behdad Bakhshinategh
Джонатан Росс
Jonathan Ross
Михал Мокни
Michal Mocny

Вероятно, вы сталкивались с тем, что страницы «заикаются» или «зависают» во время прокрутки или анимации. Нам нравится говорить, что этот опыт не является гладким . Чтобы решить подобные проблемы, команда 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.

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

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

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

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

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

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

Кадры анимации: важные обновления

Приведенный выше пример демонстрирует, что это нечто большее, чем просто requestAnimationFrame() .

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

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

Обновления основного потока и потока композитора

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Наконец, обнаружение анимации и обновлений кадров анимации по-прежнему является лишь частью задачи, поскольку оно фиксирует только количество обновлений анимации, а не качество.

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

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

И, конечно же, на сайте может быть очень плохая анимация 🙂

Старая школа строится гифка

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Интерфейс производительности

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

Интерфейс производительности

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

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

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

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

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

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

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

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

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

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

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

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

Что дальше

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

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

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

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

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

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