От автора: За последние несколько лет нам часто приходилось слышать об аппаратном ускорении и о том, как с его помощью можно улучшить анимацию на веб-страницах. С помощью ускорения анимация становится плавной даже в мобильных браузерах. Но я думаю, что есть много неопытных разработчиков, не понимающих принцип работы аппаратного ускорения, а также, как мы можем его использовать, чтобы наша анимация засияла.
Сам термин звучит как нечто крайне сложное, близкое к высшей математике. В этой статье я пролью свет на эту тему и продемонстрирую, как применять данную технику в своих front-end проектах.
Почему это должно меня волновать?
Рассмотрим простой пример анимации из нескольких шаров, расположенных один поверх другого (ось Z, нам они кажутся одним шаром). От нас требуется заставить двигаться эту группу шаров. Самый простой способ это изменять значения свойств left и top. Это можно делать через JS, но мы воспользуемся CSS3 анимацией. Обратите внимание на то, что я у себя не пишу вендорные префиксы, однако вам для полной поддержки необходимо будет использовать что-то типа Autoprefixer:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
.ball-running { animation: run-around 4s infinite; } @keyframes run-around { 0%: { top: 0; left: 0; } 25% { top: 0; left: 200px; } 50% { top: 200px; left: 200px; } 75% { top: 200px; left: 0; } } |
Ниже представлено демо, анимация запускается по кнопке через JS:
Если кликнуть на кнопку «Start Animation», то можно заметить, что анимация не такая и плавная, даже в десктопном браузере. А если открыть демо в мобильном браузере, то анимация будет далеко не 60fps. Чтобы это исправить, мы воспользуемся CSS трансформациями и функцией translate() в частности. Заменим анимацию top и left этой функцией.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
.ball-running { animation: run-around 4s infinite; } @keyframes run-around { 0%: { transform: translate(0, 0); } 25% { transform: translate(200px, 0); } 50% { transform: translate(200px, 200px); } 75% { transform: translate(0, 200px); } } |
Демо по коду выше:
Вот теперь анимация выглядит плавно. Отлично! Так в чем же разница, почему этот способ намного лучше? Дело в том, что 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 выполняется сама анимация. По завершению анимации дополнительный композитный слой удаляется, что влечет за собой еще одну операцию перерисовки.
Свойства поддерживаемые GPU
Не все изменения CSS свойств могут быть обработаны напрямую через GPU. Поддерживаемые свойства:
transform
opacity
filter
Дабы обеспечить плавность и наивысшее качество анимации, необходимо стараться использовать эти дружелюбные GPU свойства.
Принудительная отрисовка элементов в GPU
В отдельных случаях может потребоваться отрисовка элементов в GPU еще до того, как анимация началась. Данный подход поможет избежать первой перерисовки при создании нового композитного слоя. В таких случаях нам может помочь так называемый «transform hack».
1 2 3 4 5 6 7 |
.example1 { transform: translateZ(0); } .example2 { transform: rotateZ(360deg); } |
Данный код говорит браузеру, что мы хотим выполнить 3D трансформацию. Браузер заранее создает композитный слой и перемещает его в GPU, тем самым запуская аппаратное ускорение.
Данный метод может быть полезен в том случае, если отрисовка объекта слишком затратное дело, так как за ним находится еще одни объект. Вернемся к первому примеру и немного изменим его. Удалим лишние шары и оставим всего один, добавим контейнер. Зададим контейнеру фоновое изображение, размытое с помощью фильтров CSS. Шар по-прежнему анимируется при помощи left и top.
И снова шарик двигается прерывисто. Из-за размытого фона каждая операция перерисовки снижает производительность. А теперь добавим к контейнеру transform hack.
Результат уже лучше, анимация довольно плавная. Почему? Все дело в том, что размытый «тяжелый» фон переброшен в отдельный композитный слой, теперь перерисовка шарика на каждом кадре не так нагружает память.
Применяйте аппаратное ускорение с осторожностью
Бесплатный сыр только в мышеловке. С аппаратным ускорением связаны несколько проблем.
Память
Самая главная проблема это память. Загрузка слишком большого количества текстур может вызвать проблемы с памятью. На мобильных устройствах это очень критично и даже может повлечь за собой падение мобильного браузера. Помните о последствиях и не применяйте аппаратное ускорение ко всем объектам на странице.
Рендеринг шрифтов
Рендеринг в GPU влияет на сглаживание шрифтов. Это происходит потому, что в GPU и CPU разные механизмы рендеринга. Т.е. если отключить аппаратное ускорение в конце анимации, то на протяжении всей анимации текст будет размытым. Более подробно изучить проблемы рендеринга шрифтов можно в статье Keith Clark.
В ближайшем будущем
Создание отдельных композитных слоев с помощью transform hack накладывает некоторые ограничения. Безусловно, в браузерах должна появиться возможность, упрощающая данный процесс. Именно по этой причине недавно была представлена спецификация will-change property. В спецификации говорится о том, что браузеру будет сообщаться о свойствах, которые в скором времени изменят свое значение, чтобы тот заранее провел соответствующую оптимизацию. Ниже пример, свойство сообщает браузеру, что в скором времени изменится значение свойства transform:
1 2 3 |
.example { will-change: transform; } |
К сожалению не все браузеры поддерживают will-change.
Заключение
Обобщим сказанное выше:
Применение GPU может повысить качество анимации
GPU анимация на всех устройствах должна быть 60fps
Используйте дружелюбные GPU свойства
Необходимо понять, как принудительно отрисовать объект в GPU при помощи transform hack.
Если вы и раньше использовали эти советы или вам есть, что сказать, пишите в комментариях.
Автор: Artem Tabalin
Источник: //www.sitepoint.com/
Редакция: Команда webformyself.
Комментарии (1)