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

Сделать одну такую png-анимацию размером одного кадра 400 на 400 мне, как в том числе аниматору, не составляло труда. Сделать сбрасывание кубиков всегда разным представлялось задачей нетривиальной ни в одном месте. Однако замотивированный круглой суммой из волосатых рук я начал штурм, думая в первую очередь о том, как сделать игру одновременно и красивой (так как изначально я дизайнер, трехмерщик и эстэт, а программистом не стану никогда), и такой, которая загрузится меньше чем за тысячу минут.

Желая оценить объем графических файлов анимаций, я сделал вариацию падения одного кубика в полоске, чтобы потом сбрасывать несколько подобных вариаций под разными углами и, возможно, это было бы достаточно разнообразно. Когда я собрал секвенцию кадров в горизонтальную полоску, мысль о GIF, его горизонтальным сжатии и индексированных цветах зажглась сразу же и отпустила только вчера вечером.

По своему опыту специалиста по анимации трехмерных кубиков я знаю, что пока они скачут и всячески анимируются, важность качества отображения их краев и других деталей обратно пропорциональна скорости. Поэтому где-то их можно пожать по максимуму как в альфе, так и в rgb, а на кадрах, где движение успокаивается и кубик встает – делать уже на 100%. То есть png со своими огромными размерами востребован только в последних 5-10 кадрах из 50-кадровой секвенции – и только в том месте кадра, где остановился кубик. Желание пожать все, что можно, напрашивается само собой.

throw

Делая вариацию падения кубика я допустил, что потом для него можно будет сделать один альфа-канал и шесть rgb-вариантов с разными текстурами (разными финальными значениями кубика). Пока кубик летит, он летит по прямой, после первого удара он чуть отклоняется, а потом отклоняется сильно, поэтому полоска кадра получается не такой тонкой, как хотелось бы, и большая часть каждого кадра остается пустой, что болезненно для png, но не было бы проблемой для gif. В итоге со всеми ухищрениями на один вариант в формате png32 потреблялось полмегабайта килобайтов. Это не учитывая, что таких вариаций падения надо было еще, скажем, штук 10 хотя бы, и в игре ожидалась куча другой графики.

Но вдруг!


В тот момент, когда я уже вошел во вкус какие-то прекрасные люди предложили заказчику сделать заказ на 7 копеек меньше и заказ сбрился, а все написанное выше лишилось логического конца. Неприятно, да? Однако я остался с пониманием четко очерченной проблемы: альфа-канал в современных больших и красивых изображениях используется подчас до безобразия расточительно и с пониманием как эту проблему можно решить.

Я подождал год, потом сел, нашел эту статью, взял из нее весь код, вставил в случайные места точки с запятой и получился javascript-плагин, который может творить, согласно моему воспаленному воображению, чудовищные вещи с анимацией на сайте, уменьшить трафик полноцветных RGBA изображений в 2-10 раз и перевести использование больших RGBA изображений на сайте в плоскость разумного. Также в него сразу зашита функция нарезки на спрайты, поэтому он может оказаться полезен для тех счастливцев кто озабочен front-end оптимизацией.

upd
По итогам публикации и комментирования стал понятен оптимальный вариант применения плагина: по причине того что он обрабатывает изображения в ходе загрузки страницы, он будет особенно интересен для тех, кто использует карты спрайтов или анимации с прозрачностью и заморачивается предобработчиком этих карт спрайтов, а так же тем, кому нужны true-большие изображения с прозрачностью и возможностью крутить степень сжатия. Для единичных и небольших картинок подойдут тинипнг и схожие сервисы.

Матчасть


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

В ходе загрузки страницы вызывается функция alpha_spriter и в нее подаются 4 атрибута:

  1. URL rgb-файла со спрайтами
  2. URL соответствующего файла с альфа-каналом в опять же любом формате, в большинстве случаев это gif / png8.
    Оба файла должны быть на этом же домене.
  3. Javascript-объект вольной формы с разметкой спрайтов. Шутка! (заметили?) Форма достаточно точна и её пример доступен в коде плагина.
  4. Ну и функция, какую хотите по завершению

В ту же секунду функция загружает изображения, совмещает их с помощью canvas в полноценный png32 и режет на спрайты. На выходе сгенеренные изображения назначаются по id (указанным в объекте JS), заготовленным в теле документа элементам в двух вариантах: src для <img>, либо background-image (поверх уже установленных других необходимых свойств фона).

Минимальная первая рабочая версия незамедлительно опробована и успешно совмещает на живом проекте изображение танцующего снежного барса, местами напоминающего петуха и таким образом спасает it-инфраструктуру одной страны от необратимого перегрева.

compare

Прилагаю для сравнения хронометраж загрузки страницы. Сама страница, плагин и тестовые файлы могут быть найдены на гитхабе, если это здесь не запрещено. Если запрещено – просто не нажимайте, пожалуйста.

time

На всякий случай еще раз обратите внимание, что у меня канвас делал свое дело в условиях веб-окружения и только со своего origin. Этот момент уточняется.

Всё


А теперь расскажите мне, что таких решений уже 10 и они никому не нужны. Про rgb8 и про альфу в css. Ну а если серьезно, то хотелось бы в первую очередь понять, насколько это востребовано и в случае чего допиливать до уровня hi-end 3000 и делать генератор sprite map. Ну и, собственно, если у вас есть пожелания – велкам.

Спасибо!

Список литературы


Та самая статья, послужившая ускорению разработки плагина и, возможно, обусловившая его появление — спасибо, sergof!

Комментарии (13)


  1. mikenerevarin
    05.06.2015 11:43
    +3

    1. demimurych
      05.06.2015 13:03
      +1

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


      1. ionicman
        05.06.2015 13:09

        Есть такая замечательная софтина FileOptimizer, куда для png встроена куча консольных утилит (pngout и т.д.) которая подбирает из их комбинации максимально эффективную.

        Но, справедливости ради, нужно затестить. По этому предлагаю автору взять какую-либо png и сжать, выложив сюда, а потом сжать этим софтом. Будет сразу виден профит и будет с чем сравнивать.

        У меня перед выкладывание в прод, картинки прогоняются через эту штуку — в среднем, без потери качества — 30-40% он оптимизирует.


      1. mikenerevarin
        05.06.2015 13:11
        +4

        Тестовый пример в виде двух файлов весит 13.8кб и требует 3 запроса на сервер (2 картинки + js), тини сжало оригинальный файл до 15.6кб. И при этом никаких приседаний с дополнительной обработкой js-ом и всего 1 запрос.

        Я не говорю что идея плохая, просто хотелось бы увидеть более впечатляющий пример.


        1. andreiselin Автор
          05.06.2015 15:30

          Судя по всему, у нас одинаковая метода. Единственная разница — они тестируют, будет ли разумно смотреться результат с индексированными цветами RGB или его сразу жать jpgом — и ставят один из нескольких пресетов. У меня же этот этап предполагается с участием рук и собственной оценкой качества. В общем, думаю, имеет право на существование. Буду допиливать.

          Вот ссылка на их оригинальную панду: cdn.tinypng.com/images/example-orig.png?9478e6a
          Весит 56,9

          Вот ссылка на обработанное ими изображение, на котором, кстати, видны ступеньки альфа-канала на тени.
          Весит 15,4

          Вот картинка, обработанная моей методой: hello.kz/alpha_spriter/test.html
          Картинки весят 12,4
          Скрипт можно ужать до трех и получится примерно то же самое — на малых объемах
          Еще немного скачет насыщенность альфа-канала, это отчасти из-за особого мнения Стива Джобса на счет цветовых модулей маков, с которым я никак не разберусь и отчасти, конечно, из-за сырости.
          В качестве сырья использованы два jpg, с уровнем компрессии: 60 для RGB и 22 для альфа-канала — из-за чего, собственно, и нет лесенки альфа-канала.


          1. andreiselin Автор
            05.06.2015 17:52

            Ссылка на пожатую панду (отчего-то пропустилась): cdn.tinypng.com/images/example-shrunk.png?9478e6a


    1. iDennis
      06.06.2015 19:47

      pngquant


  1. GreatRash
    05.06.2015 12:21
    +1

    Ну и генератор спрайтов заодно:
    draeton.github.io/stitches


  1. Halt
    05.06.2015 13:38

    Небезызвестный papabubadiop решал эту задачу с другой стороны — советую обратить внимание на пост: habrahabr.ru/post/227577


    1. andreiselin Автор
      05.06.2015 15:34

      У нас был тоже вариант рендерить их уже в игре, но тогда мы сочли, что со стандартизацией канваса ситуация обстоит посложней чем с IOS и решили не рисковать


  1. FishDude
    05.06.2015 13:59

    Картинка примера специфичная. С другой стороны, похоже, стоит задача баланса между альфой и собственно холстом. Обычными инструментами это не решается, но… «у нас есть такие приборы»: png, 10,1 Кб — 16 цветов — из которых половина прозрачных.
    dl.dropboxusercontent.com/u/13729396/pic/habr-259613.png

    Автор просил рассказать о 10 аналогичных решениях, и о том, что они не нужны. Как пожелаете, расскажем:
    www.artlebedev.ru/tools/technogrette/img/png-2
    См. последний пример.


    1. andreiselin Автор
      05.06.2015 17:44

      Это картинка под пример с гифом.
      Признаюсь, про индексированные rgba в png8 я узнал только приступая к работе. Однако, как только их становится больше 256, происходит взрыв.
      Для таких случаев в каменте выше есть пример с пандой с tinypng, там — много цветов и все сделано через jpg


  1. vearutop
    08.06.2015 08:43

    Интересно было бы сравнить с BPG