Апаратне прискорення в CSS-анімації

15

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

Сам термін звучить як щось вкрай складне, близьке до вищої математики. У цій статті я проллю світло на цю тему і продемонструю, як застосовувати цю техніку у своїх front-end проектах.

Чому це має мене хвилювати?

Розглянемо простий приклад анімації з кількох шарів, розташованих один поверх іншого (вісь Z, нам вони здаються однією кулею). Від нас потрібно змусити рухатися цю групу куль. Найпростіший спосіб це змінювати значення властивостей left і top. Це можна робити через JS, але ми скористаємося CSS3 анімацією. Зверніть увагу на те, що я у себе не пишу вендорные префікси, однак для повної підтримки необхідно буде використовувати щось типу Autoprefixer:

.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 цією функцією.

.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. Подивимося на графік під час виконання анімації:

Апаратне прискорення в CSS-анімації

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

Апаратне прискорення в CSS-анімації

Як видно, зелених стовпців взагалі немає. В панелі розробників Chrome є ще один спосіб відстежити процес перемальовування, з допомогою опції «Enable paint flashing». Щоб активувати цю опцію, відкрийте панель розробника, натисніть на клавішу ESC, перейдіть у вкладку «Rendering». Якщо опція включена, то при перерисовывании об’єкта поверх нього буде з’являтися зелений прямокутник. У прикладі з top і left, зелений прямокутник буде протягом всієї анімації:

Апаратне прискорення в CSS-анімації

А у випадку з CSS трансформаціями, зелений прямокутник з’являється тільки на першому і останньому кадрі. Так як в прикладі з CSS трансформацією об’єкт рухається без перемальовування? CSS трансформації працюють безпосередньо з GPU пам’яттю, яка використовує апаратне прискорення, уникаючи при цьому програмного рендеринга. Розглянемо процес більш докладно.

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

Коли браузер отримує розмітку сторінки, він парсити її для побудови DOM. DOM, CSS дозволяють браузеру побудувати дерево відтворення. Дане дерево складається з об’єктів показу – елементи, які видно на сторінці. Кожен об’єкт приписаний до графічного шару, а кожен шар завантажується в GPU в якості текстури. Як і у випадку з 3D графікою графічний шар GPU можна трансформувати. Всі трансформації здійснюються за допомогою окремого конструктора шарів. Більш детально про композиції шарів у Chrome можна дізнатися за посиланням.

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

Апаратне прискорення в CSS-анімації

Зараз ви можете запитати: Коли браузер створює ці окремі композитні шари? Це робиться у випадках:

3D або перспективні трансформації (як у нашому прикладі)

При використанні тегів video і canvas

При використанні фільтрів CSS

Якщо елемент перекриває інший, розміщений на композитному шарі (тобто z-index)

Ви можете сказати «Стояти, тут використовується 2D, а не 3D трансформація». І ви будете праві. Саме тому в нашому прикладі є два моменти перемальовування об’єкта, на початку і в кінці анімації.

Апаратне прискорення в CSS-анімації

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

Властивості підтримувані GPU

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

transform

opacity

filter

Щоб забезпечити плавність і найвищу якість анімації, необхідно намагатися використовувати ці доброзичливі GPU властивості.

Примусова відображення елементів у GPU

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

.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:

.example {
will-change: transform;
}

На жаль не всі браузери підтримують will-change.

Висновок

Узагальнимо сказане вище:

Застосування GPU може підвищити якість анімації

GPU анімація на всіх пристроях повинна бути 60fps

Використовуйте доброзичливі GPU властивості

Необхідно зрозуміти, як примусово промалювати об’єкт в GPU за допомогою transform hack.

Якщо ви і раніше використовували ці поради або вам є, що сказати, пишіть в коментарях.