Xiper

Лесенка спрайтов — сложный случай поклейки

Автор: Александр Головко Дата публикации:

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

Если ты не знаком с этим методом, настоятельно рекомендую прочитать сначала статью Спрайты: меньше картинок — больше скорость. Тогда материал, изложенный ниже, будет значительно понятнее.

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

Тем не менее, иногда попадаются весьма трудные случаи, когда красиво и хорошо подклеить не получается. Вот такой вот «трудный случай» и будем сегодня перевоспитывать.

Задача

Есть группа элементов вида «пикторгаммка – текстовая надпись». Пикторгаммки расположены в левом верхнем углу надписи. Нужно поклеить пиктограммки в спрайт.

Дополнительное условие: ширина и высота надписи заранее неизвестна.

Примеры таких элементов примеры таких элементов

В поисках решения

Давай сразу оговорим, нас интересует максимально простое решение. Пикторгаммка будет фоном (background) надписи, то есть, используем только один элемент. В том числе не задействуем :before (его бы пришлось эмулировать для IE6-7).

Возьмем какой-то пример. Допустим, имеем три вида пиктограмм:

вариант с простыми короткими подписями

Соответственно есть три картинки - «плюс», «минус», «квадрат». Каждая размером 15x15 пикселей. Как же их лучше склеить? Понятно, что с точки зрения размера спрайта, оптимально будет размещать их впритык. Причем, в ряд клеить, конечно, нельзя, ведь так фон вылезет на надпись:

размещение в ряд

Пробуем в столбик:

спрайт-столбик спрайт-столбик

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

вариант с многострочными подписями слева — то, что мы хотим получить; справа — то, что получили: у длинных строчек может отобразиться «левый» фрагмент фона

В таком случае часто используется поклейка с отступами. Если точно известна ширина надписи — можно расположиться по-горизонтали (теперь отступы не позволят картинке «влезть» на текст):

горизонтальная поклейка

Если ширина неизвестна, придется клеить с отступами в столбик.

А теперь самое интересное! Какого же размера, в таком случае, должны быть отступы?

Чтобы гарантированно не появились «левые» фрагменты фона, отступ должен быть заведомо больше, чем максимальная высота надписи. Вот тут и проявляется главный недостаток такой поклейки. Я, конечно, могу сделать очень большие отступы, но размер склеенной картинки неоправданно возрастет! Следовательно, выгода от такой оптимизации стает сомнительной.

Кроме этого, остается риск, что кто-то сделает очень-очень большую надпись, которая все-таки захватит кусок лишнего фона! Ты ведь помнишь дополнительное условие — ширина и высота надписи заранее неизвестна (т.е. может быть любая).

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

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

Включаем мозг

Решение существует! Все, что нужно — составить спрайты так, чтобы места под надписи не пересекались с картинками.

Для расположения пикторгамм в левом вернем углу подойдет лесенка. Спрайт при этом будет выглядеть так:

клеем лесенкой sprite.png

На следующем рисунке цветными линиями показаны участки фона, которые получит каждый элемент.

клеем лесенкой - текст не налезет на картинки

Эти участки не ограничены по ширине и высоте, и в каждый попадает только одна (своя) пиктограммка! Задача решена.

Код

HTML я сделал просто списком. Собственно, в данном случае, это абсолютно не важно. Суть не в HTML, а в CSS и картинке.

<ul>
	<li class="plus">Плюсик</li>
	<li class="minus">Минусик</li>
	<li class="square">Квадратик</li>
</ul>

Пользуясь тем, что мы знаем размеры пиктограммок, составляем CSS (sprite.png — картинка-лесенка из рисунка выше):

li {
	background: url(../images/sprite.png) no-repeat;
	margin-bottom: 10px; /* отступ для красоты */
	padding-left: 22px; /* отступ, чтобы надпись не наехала на свою картинку */
}
.plus {
	background-position: -30px 0;
}
.minus {
	background-position: -15px -15px;
}
.square {
	background-position: 0 -30px;
}

Сразу же оговорю, что, конечно, можно было на один класс сократить, и один из background-position присвоить непосредственно li. Я этого не сделал, а ввел три класса просто для демонстрации того, что они все равнозначны и «картинки по-умолчанию» тут нет.

Демо-пример.

Проверено в:

  • IE 6-8
  • Firefox 3.0
  • Opera 9.5-10.5
  • Safari 4
  • Chrome 7

Фанатам оптимизации

Кто-нибудь может возразить, что поклейка лесенкой — дело нерациональное. Ведь если иконок много, то в склеенном файле будет «гулять» достаточно много места. Да, это действительно недостаток. Тем не менее, если клеить просто в ряд или столбик с отступами, места тоже теряется прилично, но еще и теряем гибкость (не можем реализовать корректную работу с надписью любой длины/ширины).

Так же учти, что конечный размер файла не будет слишком большим (гораздо меньше, чем суммарный размер отдельных картинок).

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

используем пустое место

Включаем мозг вместе

К сожалению, предложенный вариант поклейки лесенкой подходит, только если пиктограммка позиционируется относительно верхнего левого края элемента (не обязательно вплотную к краю — можно с фиксированным отступом). Лесенка пасует перед другими «неподдающимися» случаями, допустим, если пикторгаммка должа центрироваться по высоте элемента.

Если ты знаешь, как укротить других «неподдающихся» — пиши. Обязательно дополним материал.

Используем псевдоэлементы

Update по коментариям Seva

Еще один вариант решения поставленной задачи — использовать псевдоэлементы after или before. Суть в том, что картинка разполагается в псевдоэлементе (у которого жестко задан размер), поэтому необходимость в хитрых поклейках отпадает. Картинки можно клеить впритык.

HTML остается прежним:

<ul>
	<li class="plus">Плюсик</li>
	<li class="minus">Минусик</li>
	<li class="square">Квадратик</li>
</ul>

В CSS создаем псевдоэлементы и прописываем им размер и позиционирование (в данном случае — абсолютное, но никто не мешает задать им, например float):

li{
	margin-bottom: 10px;
	padding-left: 22px;
	position: relative;
}
li:before{
	content: "";
	width: 15px;
	height: 15px;
	position: absolute;
	left: 0;
	top: 0;
	background:url(images/sprite-2.png) no-repeat;
}
li.minus:before{
	background-position: -15px 0;
}
li.square:before{
	background-position: -30px 0;
}

Как ты, конечно, помнишь, IE 6-7 не понимают after и before. Для них придется использовать прием описанный в статье Эмуляция псевдоэлементов after и before для IE 6-7.

Эти стили нужны только для IE6-7, используй для этих браузеров отдельный CSS, подключаемый условными комментариями:

li{
	z-index: expression(runtimeStyle.zIndex = 1, insertAdjacentHTML('afterBegin', '
')); } .icon { width: 15px; height: 15px; position: absolute; left: 0; top: 0; background:url(images/sprite-2.png) no-repeat; } * html .icon { left: -22px; /* ie6 выпендрился и спозиционировал элементы немного не так, как его братец ie7. Пришлось подхачить */ } .minus .icon{ background-position: -15px 0; } .square .icon{ background-position: -30px 0; }

Демонстрационный пример.

Проверено в:

  • IE 6-8
  • Firefox 3.5+
  • Opera 10.5
  • Safari 4
  • Chrome 10

Достоинства

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

Недостатки

  • Используются псевдоэлементы, что несколько усложняет итоговый (скармливаемый браузеру) код.
  • Требуются громоздкие костыли для IE6-7 (хотя это уже скоро, надеюсь, станет неактуальным).

Вывод

При малом количестве иконок и их расположении слева от элемента замечательно подходит поклейка лесенкой — получаем чистый HTML и красивый CSS.

В случае, когда иконок много (и лесенка «сожрет» неоправданно много места на спрайте) или когда картинки позиционируются более сложно (например, центрируются по высоте элемента), можно использовать псевдоэлементы.