/* 15.04.2006 */

PNG-прозрачность в IE 5.5-6

Автор: Цыгырлаш Игорь (15.04.2006)

Сегодня мы поговорим о прозрачности в формате PNG. PNG реализует прозрачность через альфа-канал, что позволяет задавать различные степени прозрачности пикселей изображения. Многие браузеры умеют работать с данным форматом и поддерживают его прозрачность: IE 5.5-6, Mozilla 0.9+ (Firefox, Netscape 6+ ...), Opera 6+, Konqueror 3+, Safari 1.0+ ... Однако скажем спасибо IE, который не желает быть как все и не даёт нашему брату расслабиться ...

Суть проблемы

**Если вам некогда или незачем вникать в суть проблемы и метода её решения переходите к пункту "Пошаговые инструкции".

Так в чем собственно проблема? А проблема в том, что Internet Explorer 5.5 - 6, умея обрабатывать PNG-прозрачность, не делает этого просто так (более ранние версии IE не в состоянии использовать прозрачность вообще, НО все это не имеет отношения к Macintosh Internet Explorer 5.0+, в нем все ok). Изображение расположенное ниже слева демонстрирует правильное отображение PNG файла с прозрачностью, а изображение справа от него показывает как будет выглядеть картинка в IE5.5 - 6 по умолчанию (а в IE более ранних, всегда так как на второй картинке):

Правильное отображение PNG прозрачности PNG прозрачность IE5.5, IE6 игнорируют

Для того, чтобы Internet Explorer5.5-6 стал учитывать альфа-канал PNG необходимо задействовать фильтр AlphaImageLoader.

* Фильтры поддерживает только IE.

Фильтр AlphaImageLoader

Фильтр AlphaImageLoader позволяет отобразить изображение в границах объекта между фоном и содержимым этого объекта. При этом, если используется изображение в формате PNG, то задействуется его alpha-канал (а именно это нам и нужно).

* AlphaImageLoader доступен начиная с IE5.5

Установить фильтр можно через стили или через скрипт:

<div style="height:250px; width:250px;         
  filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/img/bg.png', sizingMethod='scale');">
</div>

<script type="text/javascript">
document.getElementById('elemId').style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/img/bg.png', sizingMethod='scale')";
</script>

Параметры фильтра AlphaImageLoader: аргумент src определяет адрес, по которому расположено изображение, а аргумент sizingMethod говорит что делать, если изображение не соответствует размерам элемента в который оно загружается и имеет 3 возможных значения: crop (обрезать изображение если оно больше размеров объекта родителя), image (увеличить или уменьшить объект по размеру изображения; значение по умолчанию) и scale (изменить масштаб изображения по размеру родителя).

Следует отметить, что фильтр требует, чтобы у элемента, к которому он применяется, было задано или свойство height, или width, или position со значением absolute, или writingMode со значением tb-rl, или contentEditable был установлен в true.

Так же нужно понимать разницу между фильтром и обычным фоном, если у фона есть свойства такие как: background-position, background-repeat, background-attachment, то у фильтра их НЕТ!

Решение проблемы

Так. Что мы имеем? Есть браузеры которые сами по себе делают все как надо, и встречая их мы ничего не делаем, используем изображение как обычно, а есть IE5.5-6 для которого нужно использовать фильтр AlphaImageLoader. Причем, мало просто загрузить изображение фильтром, также нужно убрать основное изображение, которое воспроизводиться IE без прозрачности и будет мешать отображаться правильному изображению.

Рассмотрим два случая: первый - изображение представлено тэгом IMG, второй - изображение является фоном какого-то элемента. В обоих случаях нужен фильтр AlphaImageLoader, но в случае тэга IMG нужно избавиться от содержимого элемента (переднего плана) , а во втором случае, нужно избавиться от фона.

Хорошо! От фона избавится весьма просто, задав свойству background-image значение "none". Но как избавиться от основной картинки тэга IMG? Единственный выход, к сожалению, это заменить исходное изображение прозрачным однопиксельным gif-ом, тогда сквозь него мы увидим png-картинку, загруженную фильтром. Для этого нужно соответственно создать этот прозрачный однопиксельный gif, и сохранить на сервере.

Что делать теоретически, вроде понятно. Теперь как все вышесказанное реализовать конкретно в виде кода, да так чтобы эта реализация оказалать максимально простой в использовании? А вот так. На странице в разделе head указываем следующий CSS-стиль:

<style type="text/css">

  селектор { filter:expression(fixPNG(this)); }

</style>

В качестве селектора указываете любой CSS селектор. Селектор определяет в каких элементах страницы будут искаться и корректироваться PNG изображения. Однако, если что такое CSS-селектор понятно, то что за "зверь" expression( fixPNG(this) ) могут понимать не все. Но давайте по отдельности разберем это выражение, мухи отдельно, котлеты отдельно. Вначале разберемся с конструкцией expression().

expression() вычисляет выражение внутри скобок и возвращает полученное значение в качестве значения css свойства, к которому эта конструкция применялась. Данная конструкция работает только в IE5.0+, другие браузеры её игнорируют.

Синтаксис:

CSS_свойство: expression( выражение)

, где выражение - это валидная строка кода на Javascript или VBSCript.

Пример:

<div style="position: absolute; left: 0; top: expression( body.scrollTop );">Эмуляция position: fixed в IE.</div>

Элемент DIV в примере всегда будет находиться в левом верхнем углу окна браузера, потому что свойству top всегда будет присваиваться новое значение, в зависимости от "проскроллености" страницы.

*Более подробно о конструкции expression читайте в документации.

Так, теперь понятно. Конструкция expression( fixPNG(this) ) вызывает функцию fixPNG, а в качестве аргумента ей передается объект к которому в данный момент применяется стиль. Как уже ясно, эта функция и выполняет всю работу по коррекции. Рассмотрим код этой функции:

<script type="text/javascript">

function fixPNG(element)
{
  //Если браузер IE версии 5.5-6
  if (/MSIE (5\.5|6).+Win/.test(navigator.userAgent))
  {
    var src;
	
    if (element.tagName=='IMG') //Если текущий элемент картинка (тэг IMG)
    {
      if (/\.png$/.test(element.src)) //Если файл картинки имеет расширение PNG
      {
        src = element.src;
        element.src = "/blank.gif"; //заменяем изображение прозрачным gif-ом
      }
    }
    else //иначе, если это не картинка а другой элемент
    {
	  //если у элемента задана фоновая картинка, то присваеваем значение свойства background-шmage переменной src
      src = element.currentStyle.backgroundImage.match(/url\("(.+\.png)"\)/i);
      if (src)
      {
        src = src[1]; //берем из значения свойства background-шmage только адрес картинки
        element.runtimeStyle.backgroundImage="none"; //убираем фоновое изображение
      }
    }
    //если, src не пуст, то нужно загрузить изображение с помощью фильтра AlphaImageLoader
    if (src) element.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "',sizingMethod='scale')";
  }
}

</script>

Код достаточно прост и прокомментирован, однако поясню один момент, а точнее строку кода:

if (src) element.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "',sizingMethod='scale')";

Эта строка "затирает" начальное значение свойства filter элемента, а именно происходит замена expression( fixPNG(this) ) на простой вызов фильтра AlphaImageLoader. При этом нет нужды возвращать какое либо значение из функции. Сделано это с целью предотвратить постоянные вызовы конструкцией expression() функции fixPNG, это просто не нужно и к тому же экономит ресурсы компьютера.

Тут знатоки особенностей браузера Internet Explorer могут сказать: "Так здесь в самый раз воспользоваться конструкцией "behavior", она и код единожды запустит, да выглядит чуть компактнее. Но behavior не подходит и вот почему: вызов "поведения" происходит после отображения объекта страницы, и поэтому вначале появится изображение без прозрачности, а лишь после будет подкорректировано (что визуально заметно пользователю). Это выглядит некрасиво.

PNG прозрачность в IEВот такой PNG фон с текстом и PNG картинкой.

Пошаговые инструкции

Как заставить IE показывать прозрачность? Сперва скопируйте и сохраните в корне своего сайта прозрачный однопиксельный gif и javascript-файл fixpng.js. Затем на своей странице между тэгами <head></head> добавьте это:

<!--[if lt IE 7]>
<![if gte IE 5.5]>
<script type="text/javascript" src="/fixpng.js"></script>
<style type="text/css"> .iePNG, IMG { filter:expression(fixPNG(this)); } .iePNG A { position: relative; }/* стиль для нормальной работы ссылок в элементах с PNG-фоном */ </style> <![endif]> <![endif]-->

Если не понимаете для чего нужны первые и последние две строки то вам сюда: "Определяем тип и версию браузера с помощью HTML комментариев".

Если вы НЕ используете PNG в качестве фона, то это все. Если же используете png в качестве фона, то установите аттрибут class="iePNG" в элементах с PNG-фоном.

Почему не работают ссылки на PNG фоне в IE?

Важно знать, что если применить фильтр AlphaImageLoader к элементу внутри которого находятся ссылки, то эти ссылки перестают работать. Т.е. та часть ссылки, которая находиться на непрозрачной области PNG картинки, ведет себя как обычный текст. Для того, чтобы решить данную проблему, следует установить этим ссылкам относительное позиционирование position: relative. В примере выше ("Пошаговые инструкции") это реализовано как .iePNG A { position: relative; }. Однако, нужно учесть, что при этом элемент к которому применен AlphaImageLoader-фильтр не должен быть спозиционирован ни абсолютно, ни относительно (т.е. у него не должно быть position: absolute или position: relative), иначе ссылки все равно не будут работать. Для того чтобы заставить работать ссылки в блоке с фильтром и позиционированием нужно разделить позиционирование и фильтр: следует создать внешний блок к которому применить позиционирование, а во внутреннем блоке установить фильтр.

* Все вышесказанное имеет такое же отношение и к элементам формы.

А это пример ссылки на PNG фоне внутри блока с позиционированием.
<div style="position: relative;"> //для позиционирования отдельный внешний блок
  //для фона отдельный внутренний блок
  <div class="iePNG" style="background-image: url(image.png); height: 142px; width:165px;">А это пример <a href="#">ссылки на PNG фоне...</a>.</div>
</div>

Почему PNG темнее в IE?

В IE есть ещё одна "особенность" (а прямо говоря - корявость) связанная с отображением PNG (PNG24), проявляющаяся в более темном отображении изображения. Причиной этому служит записанная в файл изображения дополнительная информация о гамма коррекции (chunk gAMA) (например, тот же Photoshop добавляет эту информацию). Для решения проблемы нужно "почистить" PNG-файл от gAMA chunk. Сделать это просто с помощью утилиты pngout (использовать так: pngout.exe in.png out.png). Так же можно воспользоваться и утилитой TweakPNG.
Удаление информации о гамме (gAMA chunk) в программе TweakPNG

PNG в IE7 + CSS прозрачность (opacity)

Хоть в IE7 и появилась поддержка PNG-24 с 8-битным альфа-каналом прозрачности, однако она не лишена сюрпризов. Если к полупрозрачному PNG изображению применить CSS свойство opacity (прозрачность), то полноценный 8-битный альфа-канал превращается в обычную 1-битную прозрачность (такую как в GIF или PNG-8): полностью прозрачные пикселы изображения так и остаются полностью прозрачными, а остальные накладываются на черный фон и конечно перестают быть прозрачными вообще. Для того чтобы избежать этого нужно как и раньше использовать старый добрый фильтр AlphaImageLoader. Ещё отмечу, что в IE7 так и не появилась нормальная CSS прозрачность (свойство opacity), а по прежнему используется фильтр Alpha DirectX.

-> Обсудить статью в форуме
<- Назад к списку статей
•> CSS scrollbar - разукрашиваем scrollbar средствами CSS
<• Прячем email-адреса от спам-роботов