Pull to refresh

Оптимизация рендеринга веб-страницы

Reading time 5 min
Views 42K
image

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

Начнём издалека, с той штуки с которой вы всё это читаете. Большинство экранов обновляют изображение примерно 60 раз в секунду и это создаёт иллюзию движения. При каждом обновлении изображение незначительно меняется, поэтому нам кажется что все преобразования происходят плавно. По сути речь идёт о кадрах, как в киноплёнке.

Если изображение (в нашем случае веб-страница) не успевает отрисоваться, мы можем различить что оно дёргается, плавность пропала, и мы пропускаем кадры. Чтобы показать 60 кадров в течении 1 секунды (1000мс) нам необходимо показывать новый кадр примерно за 16,6 мс. Иначе говоря, если мы видим скачки движения — новое изображение не успевает отрисоваться за 16 миллисекунд.

Чтобы определить проблему, нам надо рассмотреть этапы того как браузер создаёт страницу и понять чем занят процессор вместо полезной и нужной деятельности. Подробно весь процесс сборки страницы описан в работе Тали Гарсиель “Как работает браузер”. Если упрощать, то при загрузке страницы браузер разбирает html и css на узлы, формирует из них деревья, объединяет их, и рассчитывает то, как должен выглядеть каждый узел.

Далее происходят два процесса:
  • Layout — рассчитывается положение и размер элементов.
  • Paint — применяются стили и непосредственно отрисовываются элементы.


Когда вы изменяете что-то на странице, браузер должен снова рассчитать и обновить изменившиеся элементы. Само по себе перерисовывание элементов дорогостоящая в процессорном времени операция и её нужно избегать без лишней необходимости.

Мы можем убедиться в этом заглянув в Chrome Dev Tools и воспользовавшись Timeline (на май 2015 он выглядит примерно так).

image

Timeline позволяет записать разные активности браузера во время взаимодействия с ним и оценить сколько они занимают процессорного времени. Если речь идёт о рендеринге, то можно оценить сколько стоит процессорного времени перерисовать тот или иной элемент. Не все они при перерисовке имеют одинаковую стоимость. Какие-то более дорогостоящие и сложные, другие менее. Определяется это эмпирическим путём, записывая и изучая таймлайн.

В Dev Tools также есть опция Show paint rectangles, которая позволяет определить происходит ли перерисовка в данный момент. Элемент, который перерисовывается, подсвечивается. Включается эта опция во вкладке Rendering.

image

Рассмотрим простой пример. Допустим у нас есть некий блок, который мы анимировали достаточно простым способом — сменой свойства left у абсолютно позиционированного элемента.

.elem{
      width: 200px;
      height: 100px;
      background-color: lightgray;
      color: white;
      position: absolute;
      left: 100px;
      top: 100px;
      transition: 1s ease;
}
.elem--active{
      left: 400px;
}


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

image

Как мы можем это сделать? Если посмотреть спецификацию (http://www.w3.org/TR/css3-transforms/#transform-property), то там в описании свойства translate написано, что если у свойства есть значение отличное от пустого, то это создаст отдельный контекст, и элемент будет выведен в новый слой.

Исторически, браузеры объединяли всю веб-станицу в один слой, однако со временем возникла потребность выделять отдельно некоторые элементы и управлять ими при помощи графического процессора (GPU). Это значительно сокращает нагрузку на центральный процессор.

Перепишем наш CSS для использования с transform: translateX().

.elem--active{
      transform: translateX(400px);
}


А теперь и попробуем вызвать тоже самое взаимодействие.

image

Область перестала постоянно перерисовываться, и был создан отдельный слой. Анимация стала более плавной за счёт того что transform использует субпиксельную точность (техника обработки изображения для улучшения качества его отображения). Свойство left привязано к пиксельной сетке и движения в первом варианте были более «дёрганными».

Стоит отметить, что отдельные слои создаются также при ряде других условий. Если вспомнить о том, как и для чего появились слои, то о списке этих условий вполне можно догадаться — это все элементы предназначение которых активно изменяться и перерисовываться. Например: теги
Tags:
Hubs:
+40
Comments 16
Comments Comments 16

Articles

Information

Website
rambler-co.ru
Registered
Employees
1,001–5,000 employees
Location
Россия