Pull to refresh

Анимированный PNG в Firefox, Opera и WebKit? Легко!

Reading time 5 min
Views 17K
Однажды, от скуки, мне захотелось создать красивую полноцветную анимацию с 8-битной прозрачностью. Понятно, что GIF для этой цели никак не подходил и я стал искать альтернативы. Flash в этом качестве даже не рассматривался – слишком нагружает процессор, плохо встраивается в страницу поверх других элементов, да и стоит не у всех.

Сперва мой взгляд пал на MNG, родственника PNG. «Спецификация формата выпущена ещё в 2001 году, поэтому он наверняка реализован во всех современных браузерах!». И тут меня ждало первое разочарование. Формат оказался слишком сложным для реализации – реализация Mozilla, по их словам, содержала столько же строк кода, сколько все остальные форматы вместе взятые, за что и была удалена из их продуктов. Другие производители браузеров, похоже, даже не пытались внедрить у себя этот формат. Конечно, существует плагин для браузеров, поддерживающих плагины Netscape, но это ничем не лучше Flash. Отмечу лишь, что, возможно, формат слишком опередил свое время. Судя по описанию возможностей, это такой растровый SVG.

Дальше поиски привели меня к формату APNG. Формат прост, как два рубля: в файле записаны изменения в кадре от предыдущего с некоторыми мелкими нюансами. Но самое главное, что браузеры, не поддерживающие этот формат, отобразят статичную картинку. В целом, по возможностям формат схож с GIF. Он был предложен Mozilla как замена монструозному MNG. Проверяю в Firefox – все отлично работает. Opera – также. Открываю в Chrome и… получаю статичную картинку. Что произошло: Mozilla предложила включить APNG в спецификацию PNG. Путем голосования эта идея была отвергнута. И теперь часть браузеров поддерживает этот формат, а часть – нет.

Предо мной явственно предстала картина:
Mozilla: Ваш формат плохой, вы ничего не понимаете в анимированных PNG! Мы сделали свой, без шахмат и поэтесс. Включите его в спецификацию!
Группа разработчиков PNG: А вот вам!

Как это часто бывает, из-за людского самомнения мы уже десять лет имеем удовольствие пользоваться давно устаревшим GIF.

И тут меня посетила идея: вроде же в SVG можно вставлять растровые изображения?!

Если гора не идет к Магомету


Для экспериментов я одолжил картинку у Mozilla:
Spinfox


Разбиваем картинку на отдельные кадры:
Кадр 1Кадр 2Кадр 3
И так далее. Получилось 25 кадров.

Указываем размеры SVG, равные размеру самого большого кадра, и вставляем изображения. Порядок, кстати, не важен.
<svg xmlns="http://www.w3.org/2000/svg"
  xmlns:xlink="http://www.w3.org/1999/xlink" width="148" height="148">
  <image x="0" y="0" width="148" height="148" xlink:href="spinfox_001.png" />
  <image x="0" y="0" width="148" height="148" xlink:href="spinfox_002.png" />
  <image x="0" y="0" width="148" height="148" xlink:href="spinfox_003.png" />
  …
  <image x="0" y="0" width="148" height="148" xlink:href="spinfox_024.png" />
  <image x="0" y="0" width="148" height="148" xlink:href="spinfox_025.png" />
</svg>

Дальше я было попытался просто накладывать последовательно кадры друг на друга, но не учел прозрачности и получил солнышко из уха лисы (этот же эффект можно пронаблюдать в IE9):
Солнышко

Эффект можно использовать для реализации одного из режимов APNG: отрисовка следующего кадра поверх предыдущего.

Теперь анимируем свойство прозрачности (opacity). Можно анимировать и координаты и сдвигать кадры за пределы видимости.
Тут проблема может проявиться в том, что прозрачность будет меняться плавно и появится шлейф или мерцание. Для этого мы указываем calcMode как «discrete».
Список опорных значений: values=«1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0». Как видно, это первый кадр. Первое значение – 1 – изображение не прозрачно. На всех остальных кадрах – полностью прозрачно.
Продолжительность анимации (dur) выбираем так, чтобы при делении на число кадров получить время одного кадра. Если какой-то кадр должен находиться на экране вдвое большее время, список опорных значений придется удваивать, либо менять длительность одного значения, что несколько сложнее.
<svg xmlns="http://www.w3.org/2000/svg"
  xmlns:xlink="http://www.w3.org/1999/xlink" width="148" height="148">
  <image x="0" y="0" width="148" height="148" xlink:href="spinfox_001.png">
    <animate attributeType="CSS" attributeName="opacity" dur="1.25s"
    values="1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0"
    calcMode="discrete" repeatCount="indefinite" />
  </image>
<image x="0" y="0" width="148" height="148" xlink:href="spinfox_002.png">
    <animate attributeType="CSS" attributeName="opacity" dur="1.25s"
    values="0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0"
    calcMode="discrete" repeatCount="indefinite" />
  </image>
<image x="0" y="0" width="148" height="148" xlink:href="spinfox_003.png">
    <animate attributeType="CSS" attributeName="opacity" dur="1.25s"
    values="0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0"
    calcMode="discrete" repeatCount="indefinite" />
  </image>
…
<image x="0" y="0" width="148" height="148" xlink:href="spinfox_024.png">
    <animate attributeType="CSS" attributeName="opacity" dur="1.25s"
    values="0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0"
    calcMode="discrete" repeatCount="indefinite" />
  </image>
<image x="0" y="0" width="148" height="148" xlink:href="spinfox_025.png">
    <animate attributeType="CSS" attributeName="opacity" dur="1.25s"
    values="0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1"
    calcMode="discrete" repeatCount="indefinite" />
  </image>
</svg>

Теперь у нас остается лишь одно неудобство – анимация состоит из множества файлов. Эту проблему мы решим с помощью data:URL, встроив изображения прямо в SVG.

Итоговый результат

Размер исходного APNG: 613321 байт;
Размер SVG: 799692 байт;
Размер SVG, сжатого gzip: 601409 байт. Даже меньше исходного!
Кадры PNG были оптимизированы при помощи pngout.

Внимательный читатель мог заметить, что данная «тайная техника» не работает в IE. Могу посоветовать только отдавать ему другой код с GIF или Flash, ждать IE10 и готовить лопату.
Tags:
Hubs:
+63
Comments 56
Comments Comments 56

Articles