Repaint для больших картинок

Repaint происходит средствами процессора, браузер тратит на это определенное время. При анимации это время негативно влияет на производительность. Я уперся в эту проблему, когда мне надо было анимировать листалку из картинок большого разрешения весом 100-200kB. Причем в ряде браузеров проблема выглядела совсем катастрофически.

Эта статья не претендует на строгость изложения и на окончательные выводы. Однако хотелось поделиться находкой с сообществом. Основной вывод такой: операции с картинками надо реализовывать средствами [canvas], которая нагружает видеокарту, не надо использовать обычные теги [img], которые служат простой презентации графики.

Итак, теперь по порядку.

Имеется следующая задача: сменить одну картинку, у которой масштаб меньше 100%, на другую — с аналогичным масштабом.

Мы говорим: «Нет ничего проще! Меняем атрибут src у тега img.» Так вот, если посмотреть, как это происходит с помощью Timeline в Webkit-based браузере, то увидим, что генерируется событие repaint и для больших картинок ротация занимает приличное время.

Попробуем менять фоновую картинку, в таком случае. Масштаб можно задавать CSS3 свойством backgroung-size. Немного быстрее.

Если использовать проприетарный zoom, ситуация тоже не сильно улучшается.

Попробуем рисовать картинки на канве — все радикальным образом изменится! Repaint занимает 0ms.

В качестве наивной иллюстрации к тому, что сказано выше, может быть следующий код (использование jquery неоправданно, но для примера это не важно):

<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
</head>
<body>
<div style="display:none">
<img id="im1" src="img2.jpg"/>
<img id="im2" src="img3.jpg">
</div>
<div><span>IMG SRC</span>
<div><img class="rotor" src="img2.jpg" style="border:1px solid #000;" alt="" width="400" height="200"/></div>
</div>
<div><span>BACKGROUND</span>
<div class="rotor" style="border:1px solid #000; width: 400px; height: 200px; background: url(img2.jpg) no-repeat; background-size: 400px 200px;"> </div>
</div>
<div><span>CANVAS</span>
<div>
<canvas id="canvas" class="rotor" width="400" height="200" style="border:1px solid #000;"></canvas>
</div>
</div>
<script>
var ctx = document.getElementById('canvas').getContext('2d')
   var img = document.getElementById('im1')
   img.onload = function(){
     ctx.drawImage(img,0,0,400,200)
   }
   $('.rotor').click( function(){
   if( $(this).is('img') ) {
   $(this).attr('src', 'img3.jpg')
   }
   if( $(this).is('div') ) {
   $(this).css('backgroundImage','url(img3.jpg)')
   }
   if( $(this).is('canvas') ) {
   var img = document.getElementById('im2')
       ctx.drawImage(img,0,0,400,200)
   }  
   $(this).css('borderColor','red')
   })
</script>
</body>
</html>


В итоговой страничке при событии клика на всех трех элементах происходит ротация изображения. У меня на размере картинки 110kB получились следующие результаты, представленные на диаграмме (хочу акцентировать, что это не статистика, просто результаты для одного частного случая).

image

Данные слишком сильно разнятся, так что их сравнивать можно только в рамках одного браузера, причем сравнивать именно порядок; я сомневаюсь, что они показывают объективное время выполнения. Для webkit-браузеров значения взяты из встроенного отладчика (вкладка timeline). Firefox 7 был оценен через dynaTrace (к сожалению, этим же инструментом не удалось измерить IE9).

dynaTrace вообще выдал достаточно противоречивые результаты: поле duration, определяющее время исполнения в асинхронном режиме, было рекордным 424.54ms для “чистого” времени исполнения 3.98ms.

image

Измерение с помощью Speed Tracer (Google) тоже преподнесло сюрприз: значения представились в виде 250, 84, 84. В общем, в некотором смысле, возникает больше вопросов, чем ответов.

image

Однако, я проверил эти примерные выкладки практикой: замена тега img на canvas принесло ускорение на порядок в Chrome и лучшую производительность в Opera (которая вообще выпала из рамок исследования скорости repaint). Firefox стал при этом более стабильным. Я объяснил все это переносом вычислений с процессора на видеокарту.

Надеюсь, мне удалось убедить не использовать [img] не по назначению. С другой стороны, возникло много вопросов и недоумений по поводу измерения скорости перерисовки в браузерах. Надеюсь на комментарии, я специально привел скриншоты с измерениями. Критика методологии приветствуется!

Похожие публикации

AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 11

    +2
    Интересно сходятся мысли со вчерашней статьёй, в которой тоже использовали Canvas для отрисовки огромного полотна с картинками: "Как создать визуальную библиотеку изображений с HTML5 Canvas"
      0
      Тестировал анимацию (цветные квадратики бегают по браузеру, отталкиваясь от его стенок). Вариант в DIV'ами, когда координаты задаются с помощью MARGIN'ов в десятки — сотни раз медленнее, чем вариант с рисованием квадратиков на CANVAS. Причём, чем больше квадратиков, тем разница заметнее.
        0
        Вы случайно не облачка рисовали для нового сайта Windows Azure?
          0
          Отнюдь
        +2
        Я уперся в эту проблему, когда мне надо было анимировать листалку из картинок большого разрешения весом 100-200kB.

        Вес передаваемых картинок никакого значения не имеет — при отображении они всё равно распаковываются в набор пикселей. И чем больше этих пикселей, тем тяжелее рисовать (то есть значение имеет только ширина, высота и битность цвета).

        Однако, я проверил эти примерные выкладки практикой: замена тега img на canvas принесло ускорение на порядок в Chrome и лучшую производительность в Opera (которая вообще выпала из рамок исследования скорости repaint). Firefox стал при этом более стабильным. Я объяснил все это переносом вычислений с процессора на видеокарту.


        Вывод не совсем правильный: одно дело, когда у вас отмасштабированные картинки, которые заново масштабируются на каждом repaint-цикле (ещё нужно смотреть, какая именно область перерисовывается, потому что вполне вероятно, что браузер может перерисовывать очень большой контейнер, хотя поменялся маленький блок), другое дело, когда на canvas вы создали новое изображение, которое больше нет необходимости перерисовывать заново. Видеокарта тут не при чём.
          +1
          В предыдущей статье про Canvas автор оригинала тоже однозначно пришёл к выводу, что дело в аппаратном ускорении, поддерживаемом Canvas (строчки «Здесь мы можем увидеть внутреннюю силу HTML5 браузеров, ...»).
            +4
            Там всё-таки немного другая задача, но принцип тот же: работа не с кучей DOM-объектов в виде масштабированных картинок, а с одной большой текстурой. Это стандартная техника оптимизации, используемая во многих приложениях. Собственно, в статье это и указано:
            Основной трюк нашего приложения в рисовании только карт, которые хорошо видно на экране.

            Автор текущей статьи сделал вывод, что:
            операции с картинками надо реализовывать средствами [canvas], которая нагружает видеокарту, не надо использовать обычные теги [img], которые служат простой презентации графики.

            … при том что, например, обычная смена opacity у немасштабированной картинки будет работать практически с такой же скоростью, как и через canvas. То есть проблема тут не в том, что тэг <img> используется не по назначению, а в том, что на каждый repaint картинка дополнительно масштабируется. К тому же, аппаратное ускорение можно включить не только через canvas, но и через CSS Transforms/Transitions.
            +1
            Вопрос: а разве canvas не участвует в перерисовке всей страницы (когда она случается)? Или что имеете в виду под «repaint-циклом»?

            Поясню свой вывод. Я менял обновлял канву n раз в секунду, и это было эффективнее, чем обновлять атрибут у тега img. Т.е. n раз в секунду из кеша бралась картинка и ложилась на канву, она, очевидно, каждый раз перерисовывалась.
              +2
              canvas – не участвует, потому что содержимое этого элемента не изменяется через стандартные reflow и CSS. В остальном, надо смотреть на код теста, чтобы понять, в чём проблема.
            0
            Конкретно в случае слайдера может помочь анимация через scrollLeft/scrollTop.
              +1
              Немножко в сторону от темы, но тем не менее
              Вот здесь
              chikuyonok.ru/2010/11/optimization-story/
              статья Сергея Чикуенка о том, как они боролись с тормозами перерисовки, и почему они вообще были.

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое