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

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

Как создавался SpriteMe

Примечание: ниже находится перевод заметки от Steve Souders "SpriteMe (part 2)", в которой он рассказывает о своем новом инструменте SpriteMe. Мои комментарии далее курсивом.

SpriteMe

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

Мотивация

У меня была замечательная идея для доклада на WordCamp в прошлом мае мне предстояло публично оптимизировать тему для WordPress. В ночь перед этим (чтобы не тормозить публично) я решил основательно подготовиться. Всего за 30 минут мне удалось сделать большинство действий по оптимизации производительности (кэширование и сжатие на уровне Apache, перемещение скриптов вниз, запуск из командной строки YUI Compressor и т.д.).

Последним шагом было создание CSS Sprites из различных фоновых картинок темы. ниже описан процесс, через который я прошел:

  1. Запустить YSlow, чтобы обнаружить все фоновые картинки.
  2. Загрузить картинки и объединить их в zip-архив.
  3. Создать общую картинку-спрайт при помощи Генератора CSS Sprites.
  4. Загрузить финальную картинку на сервер.
  5. Отредактировать CSS для старой фоновой картинки, поправив background-url на новый и задав соответствующий background-position, который выдал генератор спрайтов.
  6. Обновить страницу.
  7. Визуально определить, где фон «развалился».
  8. Повторить шаги 2-7 несколько раз, пытаясь найти верную комбинацию картинок и значений background-position.

Несколько часов спустя мне удалось объединить большое количество исходных картинок в спрайт и внести необходимые изменения в CSS, чтобы сохранить первоначальный дизайн страницы нетронутым. Но это заняло так мноооого времени. Именно поэтому я и решил создать SpriteMe.

Логика создания спрайтов

Здесь Steve описывает свой подход для создания спрайтов. Его будет интересно сравнить с подходом, освещенным в этой статье.

Наиболее сложной частью создания SpriteMe было обнаружение фоновых картинок, которые могут быть объединены в спрайты и задание правил их объединения. Почти все, что я знаю о CSS Sprites, я узнал в последние несколько месяцев, поэтому для меня очень важно задокументировать эту логику и подтолкнуть гуру CSS-кода для ее анализа и высказывания своего мнения на ее счет. Ниже изложена высокоуровневая логика, которую сейчас использует spriteme.js для создания спрайтов:

  1. Пропускаем картинки, которые используют repeat-x И repeat-y. Картинки, которые повторяются по всем направлениям, не могут быть объединены в спрайт, потому что рядом лежащие в этом спрайте картинки будут видны. На самом деле часть таких повторяющихся картинок можно объединять, если четко заданы размеры области. в которой происходит повторение, и эта область не является большой: тогда можно в итоговом спрайте просто «залить» эту область повторяющейся картинкой. Критерий «большой» области легко можно установить из соотношения 1 сэкономленный запрос (в мс) = увеличенный размер картинки (время загрузки на скорости в 100Кб/с). Грубый расчет дает, что мы можем пойти на увеличение размера в 5-10 Кб для включения такой картинки, при использовании сжатия, заложенного в PNG-формат, это дает весьма значительные размеры ограничительной области, от 1000x1000.
  2. Пропускаем картинки, которые слишком «маленькие» и «узкие». Такие фоновые картинки имеют собственную высоту меньше, чем у контейнера, либо ширину корочу, чем у контейнера, для которого они используются. Картинки, которые уже и короче контейнера, должны быть расширены за счет отступов (пустого места). Если отступы получатся слишком большие, то итоговая картинка-спрайт будет очень объемной (как в размере файла, что не совсем верно для PNG, так и в объеме выделяемой под спрайт памяти), и все это отрицательно скажется на преимуществах, получающихся после создания спрайта. Фоновые картинки, которые уже или короче, чем контейнер, могут быть склеены, как описано ниже.
  3. Склеиваем картинки с repeat-x, у которых одинаковая ширина. Основная особенность таких картинок заключается в том, что у них не должно быть никаких отступов слева или справа: все, что находится сбоку, будет повторяться каждый период картинки. Поэтому картинки с repeat-x могут быть склеены только вертикально (с одинаковой шириной). SpriteMe сейчас оперирует только картинками repeat-x одинаковой ширины. Я предполагаю расширить это поведение на различные ширины, если наименьшее общее кратное всех ширин не превосходит 20 пикселей. Например, картинки шириной в 2, 3 и 9 пикселей могут быть объединены в одну с шириной в 18 пикселей. Это реализуется повторение первой картинки 9 раз, второй 3 раза и последней 2 раза. (Картинки repeat-x с шириной, большей 20 пикселей, могут быть объединены только если имеют одинаковую ширину.) Тут стоит отметить, что 20 пикселей будет довольно жестким ограничением. Учитывая особенности сжатия PNG-изображений можно закладываться на значительно большую ширину, например, в Auto Sprites это значение вообще не ограничено.
  4. Картинки repeat-y склеиваются аналогично repeat-x, только с одинаковой высотой и горизонтально. (Алгоритм полностью описан в предыдущем пункте.)
  5. Склеиваем «короткие» изображения горизонтально. Представим фоновую картинку (например, «водяной знак»), которая значительно короче, чем блок контейнера: может быть, фоновая картинка будет 10px в высоту против 500px у контейнера. Такая картинка не может быть добавлена к остальным вертикальным методом: осташиеся 490px будут заполнены остальными фоновыми картинками, расположенными ниже (однако мы можем добавить в итоговый спрайт прозрачную область указанной высоты или добавить указанную картинку в самый низ спрайта, чтобы рудиментов никаких не возникало). Но такая картинка может быть объединена с другими горизонтальным способом (те картинки, которые не короче контейнеров и не повторяются, уже были охвачены логикой более ранних пунктов).
  6. Склеиваем все остальное вертикально. Фоновые картинки, которые не подошли ни под один из предыдущих случаев, могут быть объединены вертикальным методом. Эти картинки не повторяются, И не являются слишком короткими. В результате, они могут быть примерно такого же размера, как родительский контейнер, или немного уже.

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

  • На данный момент SpriteMe помещает картинки repeat-x в один спрайт, а неповторяющиеся — в другой (вертикальный метод), и эти группы изображений никогда не пересекаются. Но изображения repeat-x могут быть добавлены к своим товарищам без повторения, если у них одна и та же ширина (в самое начало или самый конец результирующего файла). А также repeat-x могут быть откопированы, чтобы подойти по ширине, как уже описано выше. Это все приведет к уменьшению числа запросов ровно на 1. Абсолютно аналогично можно поступить с repeat-y случаями и горизонтальным методом.
  • Картинки, которые слишком «короткие» не склеиваются. но если такая картинка расположена в самом низу контейнера, то она может быть помещена в самый верх спрайта, полученного вертикальным методом. Аналогично, если мы имеем дело с «короткой» картинкой, расположенной в самом верху блока — она может находиться в самом низу вертикального спрайта. Аналогичные рассуждения можно провести и для «узких» картинок, располагая их слева или справа горизонтального спрайта. В лучшем случае это может убрать еще 4 HTTP-запроса.
  • Было замечено несколько картинок, для которых указано повторение по всем осям, но контейнер имеет те же самые размеры (или даже меньше), что и сама картинка. В этом случае мы можем включать такие картинки на общих основаниях как неповторяющиеся.

Наконец SpriteMe не обрабатывает следующие картинки:

  • JPEG. Вообще говоря, возможно склеивать JPEG-изображения, но в результате тестирования SpriteMe на популярных ресурсах было обнаружено, что результирующие спрайты значительно больше в размере, чем исходные картинки (50Кб или больше). Наиболее частой причиной этого было объединение картинок с палитрой 256 цвет и менее вместе с JPEG. Пока SpriteMe не сможет получать достоверные сведения о палитре (это проблема #17), придется пропускать JPEG-картинки.
  • картинки за файерволом. SpriteMe использует сервис coolRunnings для создания спрайтов. Это достигается путем отправки URL всех картинок на в одном запросе сервису coolRunnings. Если же картинка находится за файерволом или еще по какой-то причине запрещена к просмотру извне, то ее склеить не удается.
  • нулевые элементы. При создании спрайтов SpriteMe проверяет высоту и ширину DOM-элементов, для которых задана фоновая картинка. В некоторых случаях (выпадающие меню, всплывающие окна) такие DOM-элементы не отрисовываются и имеют нулевые ширину и высоту. Если все DOM-элементы, для которых данная картинка задана как фон, имеют нулевые размеры, то картинка в спрайт не попадает.

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

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