CSS-спрайты: оптимизация графики для web-страниц

10.04.2015
Иногда на web-странице нужно отобразить большое количество небольших фотографий, например, товаров в каталоге интернет-магазина. Если количество фотографий превышает некоторый предел, то вместо каких-то из них показывается только рамка. Перезагрузка страницы не всегда помогает отобразить их все . Может получиться так, что после перезагрузки страницы пропадают фотографии, которые отображались на ней до этого. На первый взгляд кажется, что проблема с браузером (кэшем), низкой скоростью Интернет-канала, неоптимизированной графикой, прокси, программами защиты от спама и т.п . Эти факторы, конечно, оказывают влияние не количество отображенных фотографий на странице, но проблема на самом деле заключается в другом.
Для отображения одной фотографии при помощи тэга <img> или свойством стиля “background” на сервер посылается один запрос. Значит, для отображения 700 фотографии (которых нет в кэше) на сервер отправится 700 http-запросов, что равносильно небольшой DoS-атаке на него! В ответ на это сервер посылает в браузер ответ, мол, дружище, запросы на фотографии я получил, но вот отгрузить смогу лишь некоторые из них, так что лови рамки, товарищ.
Выход из ситуации очевиден - необходимо уменьшить количество http-запросов путем размещения большого количества фотографий в одном графическом файле. То есть, скажем, вместо 100 запросов на сервер посылается один на загрузку большого графического файла, который содержит 100 фотографий (спрайтов).
Для работы данного механизма используется CSS-свойство ”background”:
<style>
  .sprite{ background-image:url(адрес изображения)}
  .image { }
</style>

  <div class=”sprite image” style=” width:X1px;height:Y1px;background-position:-Xpos1px  - Ypos1px”>Спрайт 1</div>
  <div class=”sprite image” style=” width:X2px;height:Y2px;background-position:-Xpos2px  -Ypos2px”>Спрайт 2</div>
  <div class=”sprite image” style=” width:X3px;height:Y3px;background-position:-Xpos3px  -Ypos3px”>Спрайт 3</div>
Идея, как видно, проста, но вручную прописывать параметры спрайта (ширину, высоту и смещение в исходном графическом листе) затруднительно. Можно воспользоваться on-line генераторами CSS-спрайтов, в которые нужно загрузить нужные изображения, а они уже сами выдадут необходимый программный код. Но это подойдет для статичных графических карт, когда заранее известно, какие спрайты и когда будут использоваться. Если же неизвестно, какие фотографии и когда потребуются пользователю, то необходимо генерировать графические листы “на лету,” в зависимости от запроса. В этом случае алгоритм будет таким:
  1. Отправить на сервер запрос для генерации графического листа (с помощью, например, графической библиотеки GD php).
  2. Вернуть получившееся изображение в браузер.
  3. Создать при помощи javascript новые слои <div> (при помощи метода appendChild ) или воспользоваться уже имеющимися для отображения в них нужных фотографий.
<?php
print '<style>';
print '.sprite{background-image:url(drawsprite.php?’.$Query.’)}'; // класс графического листа

/* $Query – строка запроса.
drawsprite.php – скрипт, который обработает запрос и сформирует графический лист
*/
print '.image { }';// класс спрайта
print '</style>';
?>
<div class=”sprite image” style=” width:Xpx; height:Ypx; background-position:-Xpospx -Ypospx”>Изображение 1</div>
Отрицательные значения позиционирования удивляют, но таков механизм позиционирования – двигается не окно кадра, а фон. Если нужно показать в кадре изображение справа от текущего, то фон сдвигается влево. Отсюда и отрицательные значения.
Примеры использования CSS-спрайтов и программного кода несложно найти в сети, поэтому на технике работы с ними останавливаться не буду, но расскажу о практическом применении их в своём случае - отображение на странице нескольких сотен минифотографий товаров в зависимости от выбранного раздела в каталоге товаров.
Для реализации этого алгоритма необходима генерация графического листа в зависимости от запроса.
Изначально все минифотографии были разного размера, но не превышали области 70*70px. При помощи скрипта пакетной обработки в FireWorks все минифотографии были приведены к одному размеру 70*70px, что существенно упрощало программный код для получения нужной фотографии из графического листа. Делается это просто: задаём размер холста и нужный фон, выделяем на нём всё, центрируем по вертикали и горизонтали:
  fw.getDocumentDOM().setDocumentCanvasSize({left:0,  top:0, right:70, bottom:70}) 
  fw.getDocumentDOM().selectAll()
  fw.getDocumentDOM().align("center vertical", true)
  fw.getDocumentDOM().align("center horizontal", true)
Копируем код в блокнот, cохраняем его с расширением jsf, открываем в Fireworks и сохраняем как команду. Затем запускаем пакетную обработку и через пару часов (FireWorks не эффективно работает с большим количеством фотографий в пакете) 5000 фотографии были приведены к нужному формату.
В функцию скрипта, где происходит запрос на выборку записей, прописываем показанные выше CSS-классы. Графический лист в моём случае представляет собой двумерный массив 10*y, где 10 – количество изображений в строке, а y – количество строк с изображениями, в зависимости от количества записей в выборке. В функцию отображения записей таблицы дописывается код выборки нужной фотографии.
При формировании очень большого графического изображения с шириной 700px я столкнулся с тем, что после выборки определенной фотографии остальные пошли со смещением на одну. Увеличил ширину изображения до 1400px и всё отлично заработало.
Возникает вопрос, а сколько фотографий можно загрузить таким способом? Достаточно, более двух тысяч фотографий объемом 10 Мб грузились как игрушка. При этом размер файла получился 1400*7000 px. Далеко не предел.
При желании можно побаловаться и с canvas-спрайтами при помощи элемента html5 <canvas>
CSS-спрайты часто используются для загрузки изображений для игр или для анимации навигационных элементов без мерцания. Но теперь вы видите, что область применения их гораздо шире, а вместе с таблицами на слоях или списках они позволяют легко и без проблем выводить на web-страницы большое количество графической информации.
Оглавление
Copyright © 2016