Почему перемещать элементы с помощью translate лучше, чем с position:absolute top/left

Original author: Paul Irish
  • Translation
Для перемещения элемента по экрану есть два основных способа:

  • CSS 2D-преобразования и translate();
  • position:absolute и изменение top/left.

Крис Койер недавно писал, почему лучше и логичнее использовать translate (это быстрее, и свойство position имеет большее отношение к вёрстке, а не к визуальным эффектам и анимации, в отличие от translate).

Я хочу расширить его ответ и привести несколько хороших примеров. Я записал скринкаст, в котором помощью Chrome DevTools timline рассматриваю различия между этими подходами с точки зрения производительности, особенностей рендеринга и композитинга на GPU.



Если вам нужна сокращённая текстовая версия — продолжайте читать.

Начнём с примеров, которые приводил Крис:



Это вполне корректные примеры, но они настолько просты, что оба варианта выглядят отлично. Мы возьмем что-то более сложное, чтобы как следует нагрузить браузер, и тогда разница станет гораздо заметнее (спасибо joshua за иконку макбука)



Вот так гораздо лучше. Но сначала сделаем небольшое отступление.

Привязка к пикселям


На демо видно, что верхний край макбука выглядит чётче на примере с top/left (а статья вроде бы о том, что translate лучше. Замечательно!) Это происходит из-за того, что абсолютное позиционирование привязывается к позициям пикселей, а translate использует субпиксельную интерполяцию.

Один из разработчиков подсистемы аппаратного ускорения Chrome, Джеймс Робинсон, называет это «дабстеп-эффектом», так как элементы словно вибрируют от низких частот во время движения. Если сделать смещение по одной из осей маленьким, всего в три пикселя, разница становится очень заметной:



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

Вернёмся к производительности


Если открыть timeline в инструментах разработчика Chrome, эти два примера будут выглядеть совершенно по-разному:



Вариант с top/left тратит очень много времени на отрисовку, и движение получается более дёрганым. В стилях прописаны крупные тени, а фон представляет собой сложный градиент, и всё это обсчитывается на CPU. С другой стороны, translate рисует макбук на отдельном слое, все двух- или трёхмерные преобразования делаются на GPU, что намного быстрее, и позволяет получить более плавное движение.

Ближе к концу видео я подробно останавливаюсь на этих моментах и показываю, как можно измерить время отрисовки, посмотреть, какие области перерисовываются, и в каких случаях используется GPU.

Демо


Вы можете добавить больше слоёв, чтобы эффект был более выраженным. Откройте timeline и поэкспериментируйте:



Рекомендации для анимации


  1. Используйте анимации и переходы CSS, когда возможно. Браузер очень хорошо умеет их оптимизировать.
  2. Если необходимо делать анимацию через JavaScript, используйте requestAnimationFrame вместо setTimeout и setInterval.
  3. Старайтесь не менять стили каждый кадр (как это делалось в jQuery animate()), анимации, заданные декларативно в CSS оптимизируются намного лучше.
  4. Использование 2D-трансформаций вместо абсолютного позиционирования обычно обеспечивает большую частоту кадров за счёт более быстрого рендеринга.
  5. Пользуйтесь timeline, чтобы находить и устранять проблемы с производительностью.
  6. Опции «Show Paint Rects» и «Render Composited Layer Borders» полезны, когда надо узнать, какие именно области перерисовываются.


Ads
AdBlock has stolen the banner, but banners are not teeth — they will be back

More

Comments 24

    +11
    Ага. Хром — единственный браузер и эталон.
      +3
      WebKit — в большинстве браузеров.

      Ну в IE ситуация схожа. Правда, он субъективно быстрее на этих примерах.
        –2
        «в большинстве браузеров» — то, что все, кто создают браузер не заморачиваясь берут вебкит, не делает его большинством.
          +5
          Можно просто сложить рынок браузеров Chrome + Safari — не меньше половины доли, + мобильные WebKit-based, так что можно считать WebKit массовым.

          Gecko сильно сдал позиции, но, возможно, ситуация изменится с выходом Firefox OS.

          Ну и, конечно, Trident. Internet Explorer потихоньку возвращает свою долю рынка.

          Других массовых движков нет. Даже тот же NetFront основан на WebKit.
      +7
      Ой, вы такой умный, вам, наверное, очень одиноко по жизни?

      Открыв оба линка в FF, выясняем: анимация 20 ноутбуков через top/left почти убивает Core i5/550Ti, а анимация через translate работает почти плавно.

      Проделав тот же нехитрый опыт в Safari, замечаем, что 50 ноутбуков через top/left почти вешают вкладку, а translate позволяет плавно (на глаз — плавнее хрома) анимировать 100 ноутбуков и больше.
      +3
      Получается, что ноутбук нарисован css-ом — тенюшками разными и т.п… Если просто картинку ноутбука двигать будут ли такие различия?
        +1
        Вы может быть еще gif-анимацию предложите или flash? Нельзя без извратов делать анимацию в инете! Только CSS — только хардкор!
        0
        на разрешении чуть больше стандартно-ноутбучного (>1280px) наблюдаю страшные тормоза в обоих исполнениях. система не Core i5 но и не самая слабая.
          0
          Да хоть 100 кадров в секунду с сотней одинаковых макбуков. Без скачков. Переносимость — процентов 90, тормозов-никаких. Угадайте на чем?
            +2
            w3m?
              0
              Какую-то фигню вы сказали. Даже не смешно. Я имел в виду Flash
                –1
                ага, точно, давайте лепить флеш по каждому пчиху
                  +1
                  Основной интерес рекламодателя — чтобы его рекламу увидели большинство посетителей площадки. Если площадка намертво вешает браузер — площадка перестает быть посещаемой. Если площадка не отображает баннер у большого количества посетителей — рекламодатель будет недоволен. Вот и всё. Баннеры на html и JS — быть может, лет через 10. Факт. Рисую баннеры уже много лет, и поток заказов идти не перестает, а на html5 что-то никто не просит
                    0
                    Здесь же сравнивается производительность двух методов реализации анимации, которая больше подойдет для разного рода менюшек, фильтров, эффектов и прочих плюшек. Для банеров и подобных штук эти методы еще точно не применимы, слишком много геморроя тянут за собой.
                      0
                      Но в примере же баннер с макбуком...?
                        0
                        Для сравнения производительности… На шарике то заметных лагов не было видно
                    • UFO just landed and posted this here
                        0
                        Вы сами-то думаете о чем говорите? Я слабо верю в популярность баннерной рекламы на мобильных девайсах в-принципе. И мой опыт дает мне немного права считать, что сомнения весьма имеют право на жизнь
                        • UFO just landed and posted this here
                    +2
                    Вот у меня флеша нигде нету, и, думаю, не у одного меня.
                0
                Интересно. Возьму на заметку. requestAnimationFrame() действительно лучше, нежели городить велосипеды на таймерах.
                А вот насчет translate, не очень-то: хромой на translate не обращал внимания и периодически делал «опаньки», а в огнелисе притормаживало. Совершенно аналогично выглядела картина с изменением позиционирования.
                Скрытый текст
                <html>
                <head>
                <script>
                (function() {
                var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
                	window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
                	window.requestAnimationFrame = requestAnimationFrame;
                })();
                var d = new Array(2), t0;
                function start(){
                	t0 = window.mozAnimationStartTime;
                	if(!t0) t0 = Date.now();
                	d[0] = document.getElementById("c1");
                	d[1] = document.getElementById("c2");
                	requestAnimationFrame(step);
                }
                function step(ts){
                	var dt = ts - t0;
                	var X = 100*Math.sin(dt/1000), Y = 150*Math.cos(dt/1000);
                	d[0].style.transform = "translate("+X+"px,"+Y+"px)";
                	d[1].style.transform = "translate("+(-X)+"px,"+Y+"px)";
                	/*d[0].style.top = 300 + X + "px"; d[0].style.left = 300 + Y + "px";
                	d[1].style.top = 300 - X + "px"; d[1].style.left = 300 + Y + "px";*/
                	requestAnimationFrame(step);
                }
                </script>
                <style>
                .container{
                	position: absolute;
                	width: 100px;
                	height: 100px;
                	top: 300px;
                	left: 300px;
                }
                .ball{
                	position: absolute;
                	top: 0;
                	left: 0;
                	width: 100%;
                	height: 100%;
                	border-radius: 50%;
                	background: #ff9999;
                	box-shadow: inset 10px -15px 50px 15px #990000;
                	z-index: 10;
                }
                .ballshadow{
                	position: absolute;
                	bottom: 0;
                	right: 20%;
                	width: 100%;
                	height: 30%;
                	background: black;
                	border-radius: 60%;
                	box-shadow: 0 0 10px 2px;
                	z-index: 0;
                }
                </style>
                </head>
                <body onload="start();">
                <div class="container" id="c1"><div class="ballshadow"></div><div class="ball"></div></div>
                <div class="container" id="c2"><div class="ballshadow"></div><div class="ball"></div></div>
                </body>
                </html>
                



                Лучше все-таки линейную анимацию делать через свойство animation. А в сложной — делать паузы, не вызывая requestAnimationFrame слишком часто.

                Only users with full accounts can post comments. Log in, please.