Kazan Tensor Forum
Олег Елифантьев, @oelifantiev
Компания Тензор, г. Ярославль
Full stack (JavaScript, Python)
Организатор Yaroslavl Frontend Meetup (http://yarfrontend.ru)
@oelifantiev
Менеджер: К новому году на сайт клиента нужны снежинки!
Dev: Okay boss!
$('.snowflake').each(function(){
var pos = $(this).offset();
$(this).css({
top: pos.top + ...,
left: pos.left + ...
});
});
setTimeout(animate, 0);
function animate() {
var col = document.querySelectorAll('.snowflake');
for (var i = 0; i < col.length; i++) {
var rect = col[i].getBoundingClientRect();
col[i].style.top = ..., col[i].style.left = ...
}
setTimeout(animate, 0);
}
Как обычно, нам поможет DevTools
Запишем все происходящее используя вкладку Performance в DevTools
Style recalculation — вычисление стилей, применяемых к конкретному элементу.
Это дешевая операция.
<body>
<p>
Длинный текст
<img align="right" src="..."/>
Еще текст
</p>
</body>
+ body
+-- p
+-- #text
+-- img
+-- #text
Это длинный текст, который браузер разобьет на прямоугольники (свой под каждую строку) в зависимости от размеров родительского блока, размера шрифта, гарнитуры и даже этого кота. Набор этих прямоугольников составит "дерево рендеринга". Тут еще немного текста чтобы получилось красиво…
Это длинный текст, который браузер разобьет на прямоугольники (свой под каждую строку) в зависимости от размеров родительского блока, размера шрифта, гарнитуры и даже этого кота. Набор этих прямоугольников составит "дерево рендеринга". Тут еще немного текста чтобы получилось красиво…
Layout — пересчет дерева рендеринга на основании стилей элементов и других входных параметров, например размера окна браузера.
Это дорогая операция!
Браузер всячески старается оптимизировать процесс. Например, он откладывает применение стилей и пересчет дерева рендеринга до окончания синхронного блока JavaScript. Таким образом он "пакетирует" несколько изменений в один расчет.
Но если в коде мы начинаем читать стилевую информацию, браузеру приходится применить все отложенные расчеты здесь и сейчас для того, чтобы ответить на наш запрос.
Это происходит синхронно. Все прочие процессы блокируются! Эффект сравним с работой Garbage Сollector.
var col = document.querySelectorAll('.snowflake');
for (var i = 0; i < col.length; i++) {
// read one, forced layout
var rect = col[i].getBoundingClientRect();
// write one, layout invalidated
col[i].style.top = ..., col[i].style.left = ...
}
Все очень просто!
Нужно сначала все прочитать, а потом все записать!
Спасибо, Кэп!
function animate() {
var col = document.querySelectorAll('.snowflake');
[].slice.call(col)
// read all
.map(readCoordinates)
.map(applyAnimationStep)
// write all
.forEach(applyToDOM);
setTimeout(animate, 0);
}
Включаем "Show paint rectangles"
Вместо left и top для перемещения используем CSS Transforms
transform: translate3d(x, y, z);
function animate(ts) {
var col = document.querySelectorAll('.snowflake');
[].slice.call(col)
.map(calculateNextPosition(ts))
.forEach(applyToDOM);
requestAnimationFrame(animate);
}
Все это актуально не только для анимации! Любой код, работающий с DOM, может выиграть!
Доп. материалы: http://bit.ly/render-speed
Вопросы?
Kazan Tensor Forum
Елифантьев Олег, Тензор
@oelifantiev