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

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

CSS Sprites 2.0

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

Обзор технологии

CSS Sprites, на самом деле, — всего лишь расширенное использование технологии background, заложенной еще в спецификации CSS1. Все, до чего додумалось прогрессивное человечество за эти годы — это множественный фон у элементов (как он будет совместим с концепцией CSS Sprites еще придется проверить). Основные свойства, которые мы используем для задания фонового изображения.

  • background-image — основная «рабочая лошадка». Именно за ней будущее в виде data:URI, который в конце концов победит CSS Sprites. Но произойдет это нескоро.
  • background-repeat — вторая не менее важная составляющая при использовании фонового изображения. Ведь задавая no-repeat для данного свойства, мы намеренно подчеркиваем, что допустимо использование CSS Sprites для «склейки» изображений (по умолчанию используется repeat).
  • background-position — «волшебная палочка» для CSS Sprites, позволяющая спрятать или показать определенные части фонового изображения.

Кроме заявленных свойств также есть еще несколько (например, background-color), но они к спрайтам имеют посредственное отношение.

Итак, мы можем выставлять источник изображения, правило для его повторения и его начальную позицию. Вроде достаточно?

Прикладные тонкости

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

  • Объект, полностью заполненный фоновым изображением. Здесь основную роль играют конечные размеры объекта (конечно, если изображение не повторяется по всем осям сразу: в таком случае использовать его для CSS Sprites не представляется возможным). Довольно часто фон под объектом может меняться в зависимости от каких-либо условий (преднамеренный акцент или действия со стороны пользователями), но для логики создания CSS Sprites это несущественно. Здесь же можно выделить три подслучая, соответствующих неповторяющемуся фону и повторяющему по оси X или Y.
  • Фоновое изображение заполняет не весь предоставленный ему объем (либо размеры объекта не заданы, либо заданы в относительных — em, % — единицах). Тут нам необходимо либо прикреплять повторяющееся изображение «в конец» спрайта, чтобы на той части объекта, что осталась без фонового изображения, не проявлялось никаких дефектов. Либо (в случае no-repeat) расположить изображения «лесенкой» (это особенно актуально в случае фона для элементов списка). Стоит отметить, что в зависимости от значения background-position CSS Sprites здесь могут быть как возможны, так и не возможны в принципе (например, в случае 100% 100%). Тут можно выделить еще несколько случаев, различающихся по background-position, background-repeat и линейными размерами блока.
  • Изображение является анимированным. Поскольку далее речь пойдет о применении PNG и JPEG-изображений для CSS Sprites, то анимированные изображения придется сразу выбросить из рассмотрения: поддержка анимированных PNG-изображений находится сейчас на самом зачаточном уровне в браузерах.

Все описанные примеры можно более четко структурировать по следующим группам.

  1. background-repeat: no-repeat, background-position: абсолютные числа, и заданы линейные абсолютные размеры.
  2. background-repeat: no-repeat, background-position: абсолютные числа, линейные размеры не заданы или заданы в относительных единицах.
  3. background-repeat: repeat-x, задана высота элемента.
  4. background-repeat: repeat-x, высота элемента не задана.
  5. background-repeat: repeat-y, задана ширина элемента.
  6. background-repeat: repeat-y, ширина элемента не задана.
  7. background-repeat: no-repeat, background-position: 100% 0, задана высота элемента.
  8. background-repeat: no-repeat, background-position: 0 100%, задана ширина элемента.
  9. background-repeat: no-repeat, background-position: 100% 0, высота элемента не задана.
  10. background-repeat: no-repeat, background-position: 0 100%, ширина элемента не задана.
  11. background-repeat: repeat.
  12. background-repeat: repeat-x или background-repeat: repeat-y, размеры элемента указаны в относительных единицах.
  13. background-repeat: no-repeat, background-position: в относительных единицах.
  14. изображение является анимированным GIF-файлом.

Глядя на эту спецификацию становится, в общем, понятно, в каком направлении двигаться для автоматизации создания CSS Sprites.

Практическое решение

Далее речь пойдет уже об инструменте Auto Sprites, который был положен в основу разработки Web Optimizer. После описанных выше умозаключений оставались чисто технические вопросы: как все это закодировать.

Для начала нам нужно разобрать все дерево CSS-правил, потом отобрать из них относящиеся к фоновым изображениям и линейным размерам объектов, уже потом произвести над ними требуемые действия. Идеально для этой задачи подходит CSS Tidy, который был замечательно испробован, протестирован и улучшен после пары небольших ошибок.

Дальше начинается самое интересное: как нам вышеописанные группы «склеивать»? Для этого используется следующий алгоритм:

  1. repeat-x изображения (группа 3) объединяются все вместе по вертикали. Попутно правится ширина фоновых изображений (приводится к общему знаменателю). В самое начало такого файла добавляются no-repeat изображения, подходящие по ширине (группа 1). Далее в самый них файла записывается 1 изображение из группы 4 (больше 1 все равно никуда не войдет).
  2. Производятся абсолютно аналогичные действия с repeat-y.
  3. Далее изображения из группы 7 объединяются по вертикали (0 100% означает, что фон должен быть прижат к правому краю элемента, соответственно, весь спрайт будет «прижат» к правому своему краю).
  4. Аналогично с группой 8 — прижимаем все к низу. Естественно, что для всех групп мы учитываем первоначальный background-position.
  5. Рассчитываем позиционирование для изображений группы 1 (для этого подойдет и просто перебор отсортированных по площади изображений: готовим матрицу, в которую пытаемся «вписать» очередное изображение, если не получается, то матрицу увеличиваем).
  6. Строим «лесенку» из изображений второй группы. Лесенку лучше строить внизу уже созданного спрайта из предыдущего пункта: тогда легко найти минимальный размер «дырки» между двумя группами изображений, чтобы сдвинуть «лесенку» вверх (и потом, возможно влево). Конечно, поиск наиболее оптимального расположения — нетривильная задача. Но ее можно решить и в достаточно грубом приближении, которое описано выше.
  7. Изображение из пункта 3 прикрепляются к правому верхнему углу нашего сложного изображения (результат действия пунктов 5 и 6). Так как у каждого такого изображения задана допустимая высота и все они находятся вне «рабочей» зоны остальных no-repeat-изображений, то никаких рудиментов не возникнет.
  8. Аналогичным образом поступаем с изображением из пункта 4 — его располагаем в нижнем левом углу нашего спрайта.
  9. На выходе мы получаем 3 спрайта со всеми возможными случаями. В среднем, размеры такоих спрайтов будет лишь незначительно превосходить (если вообще будет) аналогичные «ручные» аналоги (в том числе, за счет использования PNG). Естественно, что можно в автоматическом режиме пропустить через pngcrush или jpegtran. Стоит также предусмотреть, в каком виде будут создаваться полноцветные изображения: JPEG или PNG (последние больше по размеру, но гарантирует отсутствие потерь качества).

Все описанные шаги уже применены в Web Optimizer (веб-приложении для автоматизации клиентской оптимизации), одна из финальных версий алгоритма работает для инструмента Auto Sprites, а с исходным текстом можно ознакомиться в SVN.

Эту логику можно применить на любом этапе веб-разработки (как при начальном создании дизайна, так и при пост-релизной оптимизации сайта). Библиотека для автоматического создания спрайтов распространяется по лицензии MIT (как мне правильно указали на CodeCamp, она позволяет использовать продукт где угодно и как угодно, но обязательно должна присутствовать ссылка на первоисточник, даже если используется не вся библиотека, а только ее существенная часть).

Живые примеры

Все картинки получены на реальной конфигурации известных CMS.

P.S. Новости по поводу Web Optimizer будут публиковаться теперь и на Twitter.

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

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