Тормозит анимация в браузере

Обновлено: 06.07.2024

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

Сам термин звучит как нечто крайне сложное, близкое к высшей математике. В этой статье я пролью свет на эту тему и продемонстрирую, как применять данную технику в своих front-end проектах.

Почему это должно меня волновать?

Рассмотрим простой пример анимации из нескольких шаров, расположенных один поверх другого (ось Z, нам они кажутся одним шаром). От нас требуется заставить двигаться эту группу шаров. Самый простой способ это изменять значения свойств left и top. Это можно делать через JS, но мы воспользуемся CSS3 анимацией. Обратите внимание на то, что я у себя не пишу вендорные префиксы, однако вам для полной поддержки необходимо будет использовать что-то типа Autoprefixer:


Практический курс по верстке адаптивного сайта с нуля!

Изучите курс и узнайте, как верстать современные сайты на HTML5 и CSS3

Ниже представлено демо, анимация запускается по кнопке через JS:

Демо по коду выше:

Вот теперь анимация выглядит плавно. Отлично! Так в чем же разница, почему этот способ намного лучше? Дело в том, что CSS трансформации не перерисовывают объект, в отличие от анимации свойств left и top. Давайте запустим Chrome и в панели разработчика перейдем на вкладку Timeline. Посмотрим на график во время выполнения анимации:


При анимировании свойств left и top мы видим зеленые столбцы на протяжении всей анимации. Перерисовка довольно трудоемкая операция. Частота кадров ниже 60fps, а мы стремимся добиться именно этого значения. А теперь посмотрим на вкладку Timeline при анимировании с помощью CSS трансформаций:


Как видно, зеленых столбцов вообще нет. В панели разработчиков Chrome есть еще один способ отследить процесс перерисовки, с помощью опции «Enable paint flashing». Чтобы активировать данную опцию, откройте панель разработчика, нажмите на клавишу ESC, перейдите во вкладку «Rendering». Если опция включена, то при перерисовывании объекта поверх него будет появляться зеленый прямоугольник. В примере с top и left, зеленый прямоугольник будет на протяжении всей анимации:


А в случае с CSS трансформациями, зеленый прямоугольник появляется только на первом и последнем кадре. Так как же в примере с CSS трансформацией объект движется без перерисовки? CSS трансформации работают напрямую с GPU памятью, которая использует аппаратное ускорение, избегая при этом программного рендеринга. Рассмотрим процесс более подробно.

Принцип работы аппаратного ускорения

Когда браузер получает разметку страницы, он парсит ее для построения DOM. DOM и CSS позволяют браузеру построить дерево отрисовки. Данное дерево состоит из объектов отрисовки – элементы, которые видны на странице. Каждый объект приписан к графическому слою, а каждый слой загружается в GPU в качестве текстуры. Как и в случае с 3D графикой графический слой в GPU можно трансформировать. Все трансформации осуществляются при помощи отдельного компоновщика слоев. Более подробно о композиции слоев в Chrome можно узнать по ссылке.

В нашем примере с CSS трансформацией создается отдельный композитный слой, который может быть изменен напрямую в GPU. Включив опцию «Show layer borders» в панели разработчика Chrome, можно просматривать композитные слои. Каждый композитный слой помечен оранжевой рамкой. В нашем примере с CSS трансформацией каждое движение шарика это отдельный композитный слой:


Сейчас вы можете спросить: Когда браузер создает эти отдельные композитные слои? Это делается в случаях:

3D или перспективные трансформации (как в нашем примере)

При использовании тегов video и canvas

При использовании фильтров CSS

Если элемент перекрывает другой, размещенный на композитном слое (т.е. z-index)

Вы можете сказать «Стоять, тут используется 2D, а не 3D трансформация». И вы будете правы. Именно поэтому в нашем примере есть два момента перерисовки объекта, в начале и в конце анимации.


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


Практический курс по верстке адаптивного сайта с нуля!

Изучите курс и узнайте, как верстать современные сайты на HTML5 и CSS3

Свойства поддерживаемые GPU

Не все изменения CSS свойств могут быть обработаны напрямую через GPU. Поддерживаемые свойства:

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

Принудительная отрисовка элементов в GPU

В отдельных случаях может потребоваться отрисовка элементов в GPU еще до того, как анимация началась. Данный подход поможет избежать первой перерисовки при создании нового композитного слоя. В таких случаях нам может помочь так называемый «transform hack».

Petroveg

То, что ты пытаешься изобрести, создано много лет назад.

miloslovesky

То что «я пытаюсь изобрести» уже изобретено и работает некорректно в хроме. Моя задача: найти ошибку или решение проблемы

Я дал ссылку на плагин, который много лет используется на огромном количестве сайтов во всём мире. Который полностью реализует нужный тебе функционал и нужную тебе анимацию. Который нормально работает во всех браузерах. И для использования которого не требуется простыня самописного JS-кода.

А до тебя всё ещё не доходит.

Petroveg

@eandr_67 вопрос звучит «Почему анимация в хроме тормозит?», что означает — человек хочет разобраться в том, что делает, а вы наседаете со своими советами всенепременно использовать плагин. To @Petroveg. Прежде чем делать самому, имеет смысл посмотреть, как сделано у других. Чтобы не задавать потом вопросы «Почему анимация в хроме тормозит?».

Petroveg

Ну вот, пошли аргументы "сам дурак". :) Очень сомневаюсь, что Вы сами стали разбираться в предложенной мешанине CSS и JS.

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

Petroveg

Petroveg

@eandr_67 вы предлагаете решения, основанные на JS. А человек пытается сделать решение, основанное на animation. Разницу понимаете?

nanomen

В хроме в принципе какие-то проблемы с transition и animation.

Если долго не выключать браузер, он начинает жутко тормозить с css анимацией.

Сам с этим столкнулся недавно. Саму проблему так и не решил пока. Из-за чего именно тормозит. Если перезагрузить компьютер - все работает быстро и плавно.

Анимация является критичным инструментом для улучшения пользовательского опыта во многих приложениях. Существует много путей создания анимации в web, например, основанные на CSS-свойствах transitions / animations или на JavaScript (using requestAnimationFrame() ). В этой статье мы проанализируем производительность CSS и JavaScript анимаций и сравним их.

CSS transition и animation

Оба этих свойства могут использоваться для создания анимации. Каждое из них имеет своё специфичное назначение:

  • CSS transitions предоставляет простой способ создать анимацию, которая происходит при переходе от текущего состояния к конечному, например, переход от обычной кнопки к кнопке в состоянии hover. Даже если элемент в середине перехода от одного стиля к другому, новый эффект transition стартует немедленно, вместо того, чтобы дожидаться, пока запущенный ранее эффект завершится. Подробнее здесь: Использование CSS transitions.
  • CSS animations , с другой стороны, позволяет разработчикам создавать анимацию, основанную на ключевых кадрах (keyframes), которые указывают этапы, которые должна пройти анимация от начального до финального состояния. CSS animation состоит из двух компонент: описание свойства, которое указывает на анимацию, а так же набор ключевых кадров, которые указывают начальное, финальное и промежуточные состояния элемента. Подробнее здесь: Использование CSS animations.

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

requestAnimationFrame

API requestAnimationFrame() предоставляет эффективный способ создания анимаций в JavaScript. Функция (callback), которую вы передаёте в этот метод, будет вызываться перед каждой следующей отрисовкой нового фрейма. Главное отличие от setTimeout() / setInterval() (en-US) в том, что здесь вам не нужно указывать время, через которое функция запустится. requestAnimationFrame() работает гораздо эффективнее, учитывая частоту кадров и производительность системы. Разработчики могут создавать анимацию, просто изменяя стили элемента каждый раз, когда происходит подготовка нового кадра (или когда обновляется полотно Canvas или в других случаях).

Примечание: Подобно CSS transition и animation, requestAnimationFrame() приостанавливает работу, когда текущий таб переводится в фоновый режим (например, при смене фокуса)

Сравнение производительности:
transitions и requestAnimationFrame

По факту, в большинстве случаев, производительность анимаций CSS практически идентична анимациям на JavaScript. По крайней мере в Firefox. Авторы некоторых JavaScript библиотек для анимации, например GSAP или Velocity.JS, даже берутся утверждать, что их решения могут работать быстрее, чем аналогичные решения на CSS. Такое возможно, потому что CSS transitions/animations просто заново вычисляют стили элементов в основном потоке процессора сразу перед тем, как срабатывает событие repaint, что примерно то же самое, что вычислять стили заново с помощью requestAnimationFrame() . Если обе анимации выполняются в одном потоке, то разницы в производительности не будет.

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

Включение измерения частоты кадров FPS

Для начала нам нужно включить инструменты измерения частоты кадров (FPS Tools), чтобы иметь возможность видеть текущую частоту кадров

  1. В поле ввода URL наберите about:config; Нажмите на кнопку I’ll be careful, I promise!, чтобы войти на страницу конфигурации.

  2. В поле поиска введите layers.acceleration.draw-fps .
  3. Нажмите два раза на ячейку, чтобы присвоить значение true . Теперь вы видите три розовых блока в верхнем левом углу окна. Первый блок указывает FPS.

Запуск теста

Для начала, в нашем тесте мы будем анимировать 1000 элементов <div> с помощью CSS.

Нажав на кнопку, вы можете переключить метод анимации на requestAnimationFrame() .

Попробуйте запустить оба метода и сравнить FPS. Скорее всего, вы увидите, что частота кадров отличается - анимации с CSS заметно быстрее. В следующей главе мы разберём - почему.

Анимация вне основного потока процесса

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

Для того, чтобы выделить анимацию CSS в отдельный процесс, нам нужно убедиться, что изменяемые свойства не запускают этапы reflow/repaint (подробнее здесь: CSS triggers). Если изменяемые CSS-свойства не делают этого, то мы можем вынести операции по вычислению стилей в отдельный поток. Наиболее известное свойство - это CSS Transform, которое выводит элемент в отдельный слой. Если элемент представляет из себя отдельный слой, то вычисление каждого следующего кадра может быть сделано на графическом процессоре (GPU). Это радикально улучшает производительность, особенно на мобильных устройства. Подробности здесь: OffMainThreadCompositing.

Вы можете отключить выведение анимации в отдельный поток, чтобы посмотреть, как эта особенность влияет на FPS. Для этого в настройках Firefox найдите флаг layers.offmainthreadcomposition.async-animations . И переключите его в false .


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

Браузеры способы оптимизировать рендеринг не только программно, но и аппаратно. В целом, вам нужно стараться использовать CSS transitions/animations везде, где это возможно. Если же ваши анимации действительно сложны - помните, что писать анимацию на JavaScript нужно только с использованием requestAnimationFrame() .

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

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


JavaScript. Быстрый старт

Изучите основы JavaScript на практическом примере по созданию веб-приложения

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

№1 Избегайте анимации тяжелых свойств CSS

В CSS Triggers вы найдете обновленный список свойств CSS с информацией о работе, которую они запускают в каждом современном браузере, как при первом изменении, так и при последующих изменениях.


№2 Выделяйте элементы, которые вы хотите оживить на свой уровень (с осторожностью)

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

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

№3 Замените setTimeOut / setInterval на requestAnimationFrame

JavaScript-анимации обычно кодируются с использованием либо setInterval(), либо setTimeout(). Код будет выглядеть примерно так:

Читайте также: