Введение
Рисование элементов сайта или приложения может оказаться очень дорогостоящим и отрицательно повлиять на производительность во время выполнения. В этой статье мы кратко рассмотрим, что может вызвать рисование в браузере и как можно предотвратить ненужное рисование.
Живопись: Супер-быстрая экскурсия
Одна из основных задач, которые должен выполнить браузер, — это преобразование вашего DOM и CSS в пиксели на экране, и он делает это посредством довольно сложного процесса. Он начинается с чтения разметки и на основе этого создает дерево DOM. Он делает то же самое с CSS и на основе этого создает CSSOM. Затем DOM и CSSOM объединяются, и в конечном итоге мы получаем структуру, на основе которой можем начать рисовать некоторые пиксели.
Сам процесс рисования интересный. В Chrome это объединенное дерево DOM и CSS растрируется с помощью некоторого программного обеспечения под названием Skia . Если вы когда-либо играли с элементом canvas
, API Skia покажется вам очень знакомым; существует множество функций в стиле moveTo
и lineTo
а также несколько более продвинутых. По сути, все элементы, которые необходимо отрисовать, сводятся к набору вызовов Skia, которые могут быть выполнены, а на выходе получается набор растровых изображений. Эти растровые изображения загружаются в графический процессор, а графический процессор помогает, объединяя их вместе, чтобы дать нам окончательное изображение на экране.
Следует отметить, что рабочая нагрузка Skia напрямую зависит от стилей, которые вы применяете к своим элементам. Если вы используете алгоритмически тяжелые стили, Skia придется проделать больше работы. Кольт МакАнлис написал статью о том, как CSS влияет на вес рендеринга страницы , поэтому вам следует прочитать ее, чтобы получить больше информации.
Несмотря на все вышесказанное, покрасочные работы требуют времени, и если мы не сократим его, мы превысим бюджет кадра, составляющий ~16 мс. Пользователи заметят, что мы пропустили кадры, и воспримут это как лажу, что в конечном итоге ухудшит взаимодействие с пользователем нашего приложения. Мы действительно этого не хотим, поэтому давайте посмотрим, какие причины вызывают необходимость покраски и что мы можем с этим поделать.
Прокрутка
Всякий раз, когда вы прокручиваете браузер вверх или вниз, ему необходимо перерисовать содержимое, прежде чем оно появится на экране. Если все хорошо, то это будет всего лишь небольшая область, но даже в этом случае к элементам, которые необходимо нарисовать, могут быть применены сложные стили. То, что вам предстоит покрасить небольшую площадь, не означает, что это произойдет быстро.
Чтобы увидеть, какие области перерисовываются, вы можете использовать функцию «Показать прямоугольники краски» в DevTools Chrome (просто нажмите на маленькую шестеренку в правом нижнем углу). Затем, открыв DevTools, просто взаимодействуйте со своей страницей, и вы увидите мигающие прямоугольники, показывающие, где и когда Chrome нарисовал часть вашей страницы.
Производительность прокрутки имеет решающее значение для успеха вашего сайта; пользователи действительно замечают, когда ваш сайт или приложение плохо прокручивается, и им это не нравится. Поэтому мы заинтересованы в том, чтобы рисование во время прокрутки было легким, чтобы пользователи не видели зависаний.
Ранее я писал статью о производительности прокрутки , поэтому ознакомьтесь с ней, если хотите узнать больше об особенностях производительности прокрутки.
Взаимодействия
Взаимодействия — еще одна причина рисования: наведение, щелчки, касания, перетаскивание. Всякий раз, когда пользователь выполняет одно из этих взаимодействий, скажем, наведение курсора мыши, Chrome придется перерисовывать затронутый элемент. И, как и в случае с прокруткой, если требуется большая и сложная отрисовка, вы увидите падение частоты кадров.
Всем нужны красивые, плавные, интерактивные анимации, поэтому нам снова нужно будет посмотреть, не отнимают ли мы слишком много времени стили, которые меняются в нашей анимации.
Неудачное сочетание
Что произойдет, если я прокрутлю страницу и одновременно передвигаю мышь? Я вполне могу случайно «взаимодействовать» с элементом при прокрутке мимо него, вызывая дорогую отрисовку. Это, в свою очередь, может подтолкнуть меня к моему бюджету кадров в ~16,7 мс (время, которое нам нужно, чтобы оставаться ниже этого значения, чтобы достичь 60 кадров в секунду). Я создал демо-версию, чтобы показать вам, что именно я имею в виду. Надеемся, что при прокрутке и перемещении мыши вы увидите эффекты наведения, но давайте посмотрим, что из этого делают Chrome DevTools:
На изображении выше вы можете видеть, что DevTools регистрирует работу по рисованию, когда я навожу курсор на один из блоков. В своей демо-версии я использовал несколько сверхтяжелых стилей, чтобы подчеркнуть суть, поэтому я иногда превышаю свой бюджет на кадры. Меньше всего я хочу, чтобы мне приходилось выполнять эту работу по рисованию без необходимости, особенно во время прокрутки, когда нужно сделать другую работу!
Так как же мы можем предотвратить это? Как оказалось, исправить это довольно просто. Хитрость здесь заключается в том, чтобы прикрепить обработчик scroll
, который отключит эффекты наведения и установит таймер для их повторного включения. Это означает, что мы гарантируем, что при прокрутке нам не потребуется выполнять какие-либо дорогостоящие интерактивные отрисовки. Когда вы остановитесь на достаточно долгое время, мы считаем, что можно безопасно снова включить их.
Вот код:
// Used to track the enabling of hover effects
var enableTimer = 0;
/*
* Listen for a scroll and use that to remove
* the possibility of hover effects
*/
window.addEventListener('scroll', function() {
clearTimeout(enableTimer);
removeHoverClass();
// enable after 1 second, choose your own value here!
enableTimer = setTimeout(addHoverClass, 1000);
}, false);
/**
* Removes the hover class from the body. Hover styles
* are reliant on this class being present
*/
function removeHoverClass() {
document.body.classList.remove('hover');
}
/**
* Adds the hover class to the body. Hover styles
* are reliant on this class being present
*/
function addHoverClass() {
document.body.classList.add('hover');
}
Как вы можете видеть, мы используем класс в теле, чтобы отслеживать, разрешены ли эффекты наведения, а базовые стили полагаются на присутствие этого класса:
/* Expect the hover class to be on the body
before doing any hover effects */
.hover .block:hover {
…
}
И это все, что нужно!
Заключение
Производительность рендеринга имеет решающее значение для пользователей, которым нравится ваше приложение, и вы всегда должны стремиться к тому, чтобы рабочая нагрузка отрисовки не превышала 16 мс. Чтобы помочь вам в этом, вам следует интегрировать DevTools в весь процесс разработки, чтобы выявлять и устранять узкие места по мере их возникновения.
Непреднамеренные взаимодействия, особенно с элементами, требующими большого количества краски, могут быть очень дорогостоящими и снижать производительность рендеринга. Как вы видели, мы можем использовать небольшой фрагмент кода, чтобы это исправить.
Взгляните на свои сайты и приложения, можно ли им немного защититься от краски?