Статьи Архив статей

Перевод: Мациевский Николай aka sunnybear
Опубликована: 22 декабря 2008

Оптимизация изображений, часть 5: AlphaImageLoader

Примечание: ниже перевод очередной заметки "Image Optimization, Part 5: AlphaImageLoader" из блога YUI. Stoyan Stefanov на этот раз рассказывает о тонкостях применения фильтра AlphaImageLoader для IE. Мои комментарии далее курсивом.

Это пятая часть серии статей про оптимизацию изображений. С предыдущими частями можно ознакомиться по адресу:

Данная статья из серии, посвященной оптимизации изображений, рассказывает о технике, доступной только в IE, — CSS-фильтре AlphaImageLoader, — который используется разработчиками для решения проблем с прозрачностью для полноцветных PNG-изображений в IE. Основная проблема с AlphaImageLoader заключается в том, что он влияет на производительность страницы, и тем самым ухудшает пользовательское восприятие. Я утверждаю, что стоит избегать использования AlphaImageLoader во всех возможных случаях.

Маленький экскурс

Как было сказано в одной из предыдущих статей, PNG могут быть нескольких видов, которые могут быть разделены на 2 основных:

  • Индексированные (палитра), их также называют PNG8, можно использовать до 256 цветов.
  • Полноцветные PNG, которые также называют PNG32 или PNG24.

Оба формата поддерживают альфа (дробную) прозрачность и, хотя изображения в формате PNG8 превращаются в GIF-подобные в IE6 (недробная прозрачность, пример, исходник), полноцветные PNG в нем же вместо прозрачных пикселей получают отвратительный фон (источник W3C).

Проблема полноцветных прозрачных PNG в IE6

Проблема полноцветных прозрачных PNG в IE6

Используем AlphaImageLoader

IE6 (и более старые версии IE) обеспечивает решение описанной проблемы при помощи CSS-свойства filter (которое доступно только в IE). Следующий код позволит отобразить требуемую картинку для всех браузеров:

#some-element {
    background: url(image.png);
    _background: none;
    _filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='image.png', sizingMethod='crop');
}

Как можно увидеть, CSS-хак с подчеркиванием позволяет применить стили только к IE < 7, и

  1. «отменить» фон, и
  2. загрузить ту же самую картинку, используя фильтр AlphaImageLoader от Microsoft.

Мы исключили предыдущие (относительно 7) версии IE из-за того, что IE7 уже поддерживается альфа-канал для изображений, поэтому ему не нужны фильтры. (IE8 также поддерживает и уже даже изменил синтаксис фильтров.)

Самое интересное заключается в том, фильтр не изменяет саму картинку: он, скорее, меняет сам HTML-элемент, к которому применен этот стиль. Другой интересной особенностью является то, что каждый такой элемент анализируется синхронно в общем UI-потоке. Процесс применения фильтра отнимает некоторое количество ресурсов: чем больше у вас «отфильтрованных» элементов, тем медленнее протекает общий процесс. И это даже если для них для всех используется одно-единственное изображение.

Вопрос заключается в следующем: как все это влияет на общую производительность страницы?

Стоп! Первая проблема

Давайте поставим простой эксперимент: создадим страницу с CSS-фильтром и затем искусственно увеличим сетевую задержку для изображения, которое используется фильтром, в 10 секунд. Что мы получили? Не только белую страницу в течение всех этих 10 секунд, но и «заморозку» браузера: вы не можете взаимодействовать со страницей (кликать по иконкам меню, вводить адрес другого сайта и даже закрыть эту страницу), пока браузер не получит искомое изображение.

Здесь тестовый пример.

В этом примере я не использовал хак с подчеркиванием, поэтому (д)эффект проявляется и в IE7, и даже в IE8 в «режиме совместимости».

Хотя негативный эффект и сильно преувеличен с целью продемонстрировать самый неблагоприятный расклад событий. Однако сетевые задержки являются неотъемлемой частью нашей жизни. И это, возможно, самое плохое, что вы можете сделать для пользователей: «заморозить» им страницу, пока определенная картинка не будет загружена. В этой связи использование технологии DURIS (data:URI + mhtml) выступает в весьма выгодном свете: IE Будет применять фильтры с использованием той картинки, которая уже загрузилась в самом CSS-файле, никаких дополнительных задержек! Первым использовать данную технологию предложил именно посмотреть профиль bolk.

Стоит заметить, что параллельные загрузки не блокируются. Браузер по-прежнему запрашивает и доставляет другие компоненты страницы в фоновом режиме, просто не происходит последовательного обновления страницы. Можно представить это и следующим образом: IE Не показывает страницу, пока CSS-файл не загрузится полностью, до самого последнего байта (более подробная информация). А поскольку CSS-файл зависит от фильтруемой картинки, то отображение страницы блокируется, пока эта зависимость не будет удовлетворена.

Но что происходит, если на странице используется несколько фильтров AlphaImageLoader? Все они будут обработаны последовательно, поэтому проблемы умножатся. Если, к примеру, у вас на странице 5 картинок, и у каждой задержка в 2 секунды, то браузер «заморозится» на 10 секунд.

Время и память — проблемы #2 и #3

Следующим отрицательным эффектом использования AlphaImageLoader будет увеличение объема используемой памяти для обработки и применения фильтров. В наши дни можно предположить, что на пользовательских компьютерах практически неограниченный объем памяти, однако, более старые компьютеры (на которых, скорее всего, и запускается IE6) не обладают таким обширным ресурсом.

В конце концов, мы заинтересованы, в первую очередь, в производительности, а производительность измеряется во времени загрузки страницы в браузере. Давайте тогда измерим, сколько времени и памяти уходит на фильтры.

Для начала мы создадим реперную страницу: на ней будет расположено сто <div>, в каждом из которых будет находиться нефильтрованная картинка. Затем создадим вторую страницу, на которой будет применен фильтр (ко всем 100 div один и тот же). К счастью, 100 элементов с фильтром довольно сложно найти на обычной странице, однако, небольшое преувеличение в данном случае поможет уточнить наши измерения.

Время замеряется от начала страницы (в самом верху страницы, до вызова всех элементов, устанавливается метка времени) до события onload. После того как картинки закэшируются, этот промежуток времени будет корректно отображать ресурсы, затрачиваемые на применение фильтров, поскольку время на загрузку картинок и страницы тратиться уже не будет. Потребление памяти измерялось при помощи инструмента ProcessExplorer: бралась дельта процесса до загрузки страницы и после. Таким образом мы устанавливали «цену» отображения страницы.

Ниже приведены усредненные результаты после 10 запусков в IE6 на компьютере с двумя 2GHz CPU и 500 Мб оперативной памяти. На менее мощном компьютере потери при загрузке страницы будут еще больше.

Тестовые результаты для AlphaImageLoader
Тестовая страницаВремя, секундыПамять, Мб
Тест #1: никаких фильтров0,0310,6
Тест #2: фильтры0,84446,8

Как можно видеть из полученных результатов, AlphaImageLoader крайне отрицательно влияет на загрузку страницы: наша тестовая страница загружалась в 27 раз медленнее и отъедала в 78 раз больше памяти (в пересчете на одну картинку получится 8 мс задержки и 0,5 Мб памяти — с этим еще можно жить :). Полностью доверять этим результатам, естественно, нельзя: это только одна картинка, протестированная на одном компьютере (относительно мощном и незагруженном). Если у вас будет несколько различных картинок, применяемых к различным элементам на различных машинах, результаты будут очень сильно колебаться. Особенно это касается тех случаев, когда у нас меньше оперативной памяти или ресурсов процессора, или когда мы столкнемся с большими сетевыми задержками (проблема #1). Но наш пример, тем не менее, замечательно иллюстрирует описываемую проблему:

  • AlphaImageLoader тормозит и требует много памяти.
  • Он применяется к каждому элементу, а не для каждой фильтруемой картинки.

Даже если вы используете CSS Sprites и используете их для различных элементов (спрайты с альфа-фильтрами сложны, но возможны), то отрицательные эффекты будут для всех элементов в совокупности, вне зависимости от числа используемых спрайтов.

Материал для изучения: поиск Yahoo!

Лабораторные тесты могут дать нам только представление о том, насколько AlphaImageLoader Дорого обходится. Можно рассмотреть несколько тестовых случаев, что того, чтобы вычислить «цену» применения фильтра для каждого элемента, но нет ничего лучше, чем тестирование на реальных проектах, на которые приходят миллионы запросов со всех концов земного шара из различных браузеров, пользовательских компьютеров и различных типов подключения.

На странице результатов поиска Yahoo! был представлен полноцветный PNG-спрайт и применялся AlphaImageLoader для достижения прозрачности (более старая версия спрайта по-прежнему доступна, если это настолько интересно). Замена полноцветного PNG его ближайшим аналогом в виде PNG8 (как уже было упомянуто в предыдущих статьях) уменьшило время загрузки примерно на 50–100 мс дял пользователей IE5 и 6. 100 мс могут показаться совсем небольшой величиной, но для страницы, загружающейся примерно за секунду, это будет около 10%. Также согласно материалам Amazon, потеря 100 мс в загрузке страницы приводит к 1% снижению продаж (даже для страниц Amazon, которые перегружены содержанием). Целесообразность замены одной картинки для роста продаж на 1% не вызывает особых сомнений.

А что дальше?

Наилучшим решением будет совсем избегать использования AlphaImageLoader и находить время (как сделали в команде Y!Search) для создания PNG8-аналогов, которые относительно хорошо выглядят в IE6 и замечательно выглядят в остальных браузерах. Каким образом мы можем создать такие (gracefully degrading) PNG8-аналоги? Ну что же, для начала нам потребуется GIF-изображение, в котором пиксели будут либо полностью прозрачны, либо полностью цветными. После того, как мы добьемся его приемлемости (чтобы оно хорошо выглядело в IE6), мы можем последовательно улучшить его, добавляя полупрозрачность на границы (чтобы гладить углы или другие части изображения, которые от этого выиграют). К несчастью, насколько я знаю, Fireworks является единственным программным продуктом, который позволяет управлять альфа-прозрачностью в PNG8. Также можно попробовать использовать утилиты командной строки, например: pngnq или pngquant. Хотя стоит заметить, что автоматизированный перевод PNG-изображений из полноцветных в индексированные может не всегда достигать поставленной цели, и вполне возможно, что придется выбирать пиксели, которые будут полностью цветными, вручную.

Также есть такие случаи, когда вы не сможете использовать PNG8 и придется обратиться к AlphaImageLoader — например, когда все пиксели в исходном изображении полупрозрачные (представьте кнопку «Проиграть» поверх видео-изображения). У Dave Artz из AOL есть еще некоторые примеры, когда обычного PNG8 не будет достаточно. В некоторых случаях (но только после того, как вы превзойдете дизайнера в попытке преобразовать прозрачные изображения) используйте хак с подчеркиванием (_filter), чтобы не усугублять ситуацию для пользователей IE7.

Иногда вместо PNG8 разработчики применяют GIF для IE6 и полноцветные PNG для всех остальных. Но это является излишним: при помощи PNG8 вы можете добиться и двоичной (прозрачно или нет) и альфа-прозрачности.

Дополнительные преимущества использования PNG8:

  1. PNG8 обычно меньше по размеру, чем полноцветные,
  2. вам нужно только одно изображение для всех браузеров,
  3. чистый CSS без хаков, ветвлений или тегов для определенных браузеров.
  4. ability to repeat background

Прозрачность при помощи VML

VML предоставляет еще один способ сделать в IE полноцветный PNG прозрачным. Также он решает некоторые другие проблемы: альфа-прозрачность, производительность и повторение фона. К несчастью, за все надо платить: VML заставляет использовать нестандартную разметку (или зависимость от JavaScript, если генерировать ее при помощи скриптов и не засорять исходную разметку) и более изощренный CSS. Ниже приведен пример использования.

Если, к примеру, у вас есть пустой div, вам нужно обернуть его в один VML-тег :rect (или :shape) и один элемент :fill, например:

<v:rect>
    <v:fill type="tile" src="alphatest.png">
	<div>&nbsp;</div>
    </v:fill>
</v:rect>

Однако, где-нибудь раньше в разметку вам нужно будет определить само пространство имен VML:

<xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" />

И добавить в таблице стилей:

v\:rect  {
    behavior:url(#default#VML);
    width: 100px;
    height: 100px;
    display: block;
}

v\:fill  {
    behavior:url(#default#VML);
}

Тестовая страница со 100 VML-элементами rect загружается за 0,094 секунды (почти в 10 раз быстрее, чем при помощи фильтров) и используется не более 4 Мб памяти (в 10 раз меньше, чем на отфильтрованной странице).

Как вы видите, данное решение добавляет свою разметку и CSS исключительно для IE, однако, является полноценной заменой AlphaImageLoader и позволяет избежать всех его минусов.

(Объявляю благодарности этой заметке от Drew Diller, а также HTML Remix, которые случайно обнаружили описываемый эффект, когда работали над другой проблемой: скругленные уголки при помощи VML, — решение которой изложено на snook.ca)

Постскриптум. А что другие фильтры?

Кроме AlphaImageLoader существует еще несколько фильтров. Наиболее популярным из них является фильтр прозрачности.

Например, для задания у элемента прозрачности в 50%, разработчики используют следующие свойства:

  • opacity: 0.5 (стандартное),
  • -moz-opacity: 0.5 (ранние версии Mozilla до Firefox 0.9), и
  • для IE, filter: alpha(opacity=50).

Быстрое тестирование под IE6 продемонстрировало, что фильтр прозрачности не настолько тормозит, как AlphaImageLoader, но все равно делает страницу медленнее и занимает такой же объем памяти. В данном тесте использовался просто цвет фона, а не картинка. Однако последняя продемонстрирует те же самые результаты.

Тестовые результаты для фильтра прозрачности
Тестовая страницавремя, секундыпамять, Мб
Тест #3: 100 div без прозрачности0,0160,2
Тест #4: 100 div с прозрачностью0,09346,7

Читать дальше

Все комментарии (habrahabr.ru)