• Спрайты в вебе. Часть 1

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

    Что такое спрайты?

    Думаю, вы и без меня знаете, что это такое. Спрайты — это несколько небольших картинок, объединенных в один графический файл.


    Спрайт с сайта «Житлобуд»

    Такой подход дает сразу несколько преимуществ. Во-первых, объем одного файла как правило меньше, чем сумма объемов отдельных файлов. Это можно объяснить снижением издержек на сохранение одного изображения: всякие там заголовки, указывающие на формат изображения, или общая палитра цветов, если речь идет об индексированном PNG или GIF. Бородатые кодеры могут вспомнить рекомендации 90-х годов по разбивке одного большого файла на несколько мелких, чтобы создать иллюзию у пользователя, что сайт грузится быстрее :) Такой подход создавал именно иллюзию, так как суммарный объем нескольких файлов был больше, чем объем одного файла, а сама картинка грузилась дольше.

    Во-вторых — самое главное — сокращается количество HTTP-запросов к серверу. Некоторые ребята не обращают на это внимание, а зря: зачастую отдельный запрос обходится намного дороже, чем десяток «лишних» данных в одном изображении. Не буду подробно расписывать этот момент, так как это тема для отдельного разговора.

    Итак, мы уже знаем, что спрайты — это круто и модно. Ограничено ли применение спрайтов только группировкой разных изображений в одно?

    Назад в прошлое

    Будучи неопытным юнцом, я очень любил играть в игры. Больше всего меня интересовало то, как же эти игры делаются. Тогда еще не было повсеместного использования 3D-графики, все игры, в которые я играл, в основном были 2D-аркадами (Another World, Commander Keen, Moles, Prince of Persia, эх…), активно использующими спрайты. Вообще, слово «спрайт» получило широкое распространение именно благодаря видеоиграм, и именно видеоигры больше всего повлияли на меня как на веб-разработчика.

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

    Раскадровка


    Слон с сайта «Паритет98»

    Раскадровка, по сути, представляет собой набор кадров для анимации, сохраненных в одном файле. Для такого спрайта пишется контроллер на JavaScript, который управляет скоростью и направлением смещения спрайта. По сравнению с обычной GIF-анимацией это дает нам следующие преимущества:

    1. Можно использовать JPEG или полупрозрачный PNG.
    2. Более широкие возможности по оптимизации изображения.
    3. Можно самостоятельно управлять скоростью и направлением анимации, имея один и тот же набор кадров (то есть используя всего одно изображение).

    При создании такого спрайта нужно помнить следующее. Для начала нужно выбрать размер кадра; такой, чтобы в нем без проблем уместилась любое изображение из раскадровки +10-20% свободного пространства по бокам для надежности. Это позволит в дальнейшем без особых хлопот модифицировать изображение.

    Затем нужно точно совместить изображения в кадрах, чтобы при анимации не были заметны рывки. Если все сделаете правильно, то на создание JS-контроллера уйдет минимум сил и времени.

    Циклический спрайт

    Этот тип спрайтов в основном применяется для создания механизмов наподобие прелоудера: некая повторяющаяся текстура на блоке неопределенной ширины.

    Тут, конечно, руки так и чешутся использовать GIF-анимацию, однако перфекционистов вроде меня может не устроить плавность движения. Сравните:

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

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

    Слои

    Иногда случается так, что для изображения не получается выбрать самый оптимальный формат. Наиболее яркий пример — фотография с полупрозрачными декорациями:

    В этом случае намного эффективнее будет разделить изображение на два слоя: первый — сердцевина — сохранить в JPEG, а второй — окантовка — в полупрозрачный PNG.

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

    Естественно, этот пример довольно простой и примитивный: уверен, что многие так и поступили бы с этой картинкой. Наибольший эффект от такого типа спрайтов достигается в том случае, когда сердцевина имеет не прямоугольную форму. Хороший пример: раздел «Отделение Imperia» сайта Imperia Private Banking:

    imperia-branch

    Огромный узор на двери был разделен на 2 слоя: сердцевину и окантовку, каждый слой был по-своему оптимизирован, что в итоге позволило мне сэкономить около 200 КБ. Как быстро разделить изображение на два таких слоя я писал в статье «Про PNG. Часть 2».

    Векторные маски

    К сожалению, не всегда подобные картинки можно эффективно разделить по слоям. Например, когда сердцевина не прямоугольной формы содержит фотографическое изображение. В этом случае на помощь приходят современные браузерные технологии, а именно SVG, Canvas и VML. С помощью них можно наложить векторные маски на прямоугольную JPEG-картинку, добившись необходимого внешнего вида. Для этих целей я написал специальную библиотеку под названием ictinus, которая занимается решением таких задач. Хороший пример использования использования спрайтовых масок (и моей библиотеки :) ) — главная страница сайта «Аймобилко»:

    imobilco

    В середине страницы находится красивый тизер продукта, у которого изображение должно принимать «неправильную» форму (в данном случае — коробка компакт-диска с загнутым углом). Так как фон не однородный (и тоже, кстати, меняется), единственным очевидным способом решения этой задачи было создание отдельного PNG-изображения весом более 100 КБ для каждого такого продукта. Меня такой расклад не устраивал, поэтому я решил в качестве эксперимента применить ictinus (на тот момент был в состоянии очень сырой альфа-версии). На прямоугольное JPEG-изображение накладывается векторная маска (в зависимости от типа продукта), затем под эту картинку ставится PNG-тень. Результат оказался более чем убедительным. Помимо сильной экономии на траффике пользователя я сэкономил нервы заказчику: теперь ему достаточно любому продукту в админке поставить галочку «Показывать на главной», а не готовить в фотошопе новые картинки.

    ***

    С типами спрайтов, пожалуй, всё (ничего не забыл?), в следующей части рассмотрим способы хранения данных в спрайтах и некоторые рекомендации по использованию.

    Скачать JS-котроллер для спрайтовой анимации



    Метки: , , , ,
  • 44 комментария

    1. Сергей
      27 мая 2009

      Спасибо Сергей =) Интересно, но для полного уяснения мне потребуется ещё пару раз точно перечитать :) Что я бы хотел услышать про спрайты – вот допустим есть картинка-спрайт, в которой есть разноцветные части сайта (закругления, иконки, лого, что угодно). В таком случае у меня появляется вопрос, как лучше оптимизировать такое изображение так чтобы и вес поменьше и качество получше. Из уже ранее услышанного я могу предположить, что решение такое – http://chikuyonok.ru/2009/04/photoshop-for-webdevs-4/ ? Или есть ещё другие способы?

    2. 27 мая 2009

      1. А зачем нужны отступы (запас) у кадров в спрайте?
      2. Если у гифа сделать задержку в 1 мс, а не 0, то анимация будет плавной :-)

    3. 27 мая 2009

      тут всё ясно, хочу заковыристых продолжений :) опыт видео-монтажа просто так не проходит

    4. Руслан Юлдашев
      27 мая 2009

      Валентин, отступы нужны чтобы можно было задавать всякие padding-и элементу и при этом соседние кадры не показывались. Особенно это актуально для всяких ссылок-иконок, активная область которых больше, чем сама картинка.

    5. Сергей Чикуенок
      27 мая 2009

      как лучше оптимизировать такое изображение так чтобы и вес поменьше и качество получше

      Об этом в следующей части напишу (если мое исследование закончится успешно)

      Если у гифа сделать задержку в 1 мс, а не 0, то анимация будет плавной

      Поставил, ничего не изменилось:

    6. egorinsk
      27 мая 2009

      Насчет разделения картинок — интересно, и про маску на аймобилко тоже интересно )) Тоже приходилось при сжатии картинки в джипег, текст жать отдельно в пнг, так как джипег его портил.

      А что касается спрайтов — иногда объединение картинок приводит к проблемам типа нехватки цветов в пнг-8 ((

      И что касается уменьшения запросов к серверу — помогают правильные HTTP-заголовки на картинках, иначе браузеры на кадой странице будут посылать запросы только чтобы получить в ответ 304.

    7. 27 мая 2009

      Опять ты феерично вернулся на арену своего блога. Вот я как-нибудь брошу все и буду реализовывать твои примеры, ты так и знай.
      Ну и да – круто, как обычно. Пиши, блядь, уже книгу!

    8. Сергей Чикуенок
      27 мая 2009

      Я не могу придумать тему для книги. Да и сомневаюсь, что это будет кому-то выгодно.

    9. 27 мая 2009

      Cпасибо, очень интересно.

      Непонятно только, где дизайнер, который рисовал Аймобилку, видел коробку компакт-диска с загнутым углом :)

    10. artwalek
      27 мая 2009

      Да, тоже бы купил вашу книгу.

      Очень интересные у вас темы.

    11. 29 мая 2009

      Тема для книги – та же, что и для блога.
      “Библия веб-дизайна. Евангелие от Чикуенка”

      Читать/покупать будут :) уверен

    12. Сергей Чикуенок
      29 мая 2009

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

    13. 17 июня 2009

      Спасибо за действительно интересную статью. Я в этом направлении новичок, так что подчерпнул много интересных идей. Ну и блог ваш уже в закладках, буду обязательно читать!

    14. 17 июня 2009

      На тему деления картинки на jpeg и png+alpha – я еще заливаю освободившееся место на png имеющимся на получившейся картинке цветом. пара байтов не помешает :)

    15. 17 июня 2009

      egorinsk: на тему правильных заголовков не расскажите?

    16. Дмитрий
      18 июня 2009

      Используйте задержку между кадрами в GIF 0.8 секунд, будет 1 в 1 как в вашей анимации, сделанной средствами js.

    17. Дмитрий
      18 июня 2009

      *милисекунд, извините.

    18. Сергей Чикуенок
      18 июня 2009

      А можете пример прислать?

    19. Дмитрий
      18 июня 2009

      отправил на serge.che@gmail.com

    20. farik
      24 июня 2009

      не нашел в документации поэтому спрошу здесь
      ictinus может кроме наложения маски(обрезания) трансформировать картинку? ну как пример вот наложение картинки на ноутбук http://s4d.com.ua/images/book-african.png

    21. Сергей Чикуенок
      24 июня 2009

      Трансформировать — нет. К тому же в приведенном примере трансформация далеко не тривиальная, так как ей не подойдет стандартная аффинная трансформация.

    22. Алексей
      17 сентября 2009

      Сергей, здравствуйте.
      Мне не удается назначить более одной маски Иктинусом под ИЕ. Ну то есть в коде я пишу конечно, но скрипт маскирует только последнее указанное изображение (больше одной картинки не обрабатывает). При этом он перемещает мою последнюю картинку на место первой, указанной скрипту для обработки (для всех изображений указаны разные координаты). Чем починить?

      П.С.Библиотека классная.

    23. Алексей
      18 сентября 2009

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

    24. Сергей Чикуенок
      18 сентября 2009

      Можете прислать код по почте? Насчет кликабельности: так вряд ли получится, но можно поступить иначе. Вы ставите прозрачную картинку поверх остальных, ей указываете imagemap, а потом создаете маску для иктинуса на основе координат этой карты. По такому принципу работают подсветки этажей на сайте Феликс Завойски (домик слева).

    25. Алексей
      18 сентября 2009

      Отправил.
      Насчет кликабельности так и думал сделать, по другому не знаю как.

    26. egorinsk
      25 сентября 2009

      Блин, отвечаю в старый тред, но все же напишу.

      > И что касается уменьшения запросов к серверу — помогают правильные HTTP-заголовки на картинках, иначе браузеры на кадой странице будут посылать запросы только чтобы получить в ответ 304.

      > Vladimir Sobolev 17 июня 2009
      > egorinsk: на тему правильных заголовков не расскажите?

      Я имел в виду, что для статики принято выставлять HTTP-заголовки Last-Modified и Expires, первый — чтобы браузер мог не скачивать заново картинку, если она не изменилась, при обновлении страницы, второй — чтобы браузер *вообще* не посылал запросов, пока картинка не изменится. Поясняю. Если вы не отдаете заголовок Expires, большинство браузеров при каждом открытии страницы поылают запрос на скачивание всех картинок с этой страницы (даже если эта картинка и так лежит в кеше браузера, он хочет проверить что она не изменилась), пусть веб-сервер и отвечает на них «304 Not Modified», все равно запросы делаются, загрузка замедляется (т.к. число одновременных запросов ограничено).

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

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

      Подробнее, как все это делается, ищите где-то на сайте webo.in в статьях :)

      Ах да, не используйте для картинок имена с параметрами (image.png?121212) как советуют некоторые ,такие картинки браузер может отказаться кешировать.

    27. 12 ноября 2009

      Вопрос от новичка в JS.
      Можно ли в контроллере ограничить кол-во кадров для анимации?
      Допустим, при клике пролистывать только 5 кадров вперед, при повторном клике – еще 5. И тп.

      пс. спасибо, замечательный контроллер

    28. FXIX
      8 января 2010

      Сергей, в Safari 3.0.3 ваш пример с циклическими спрайтами не работает

    29. Inqy
      31 марта 2010

      Здравствуйте, Сергей. На гуглокоде вас спросили насчёт иктинуса:
      “Comment by goncharenko.roman, Aug 21, 2009
      Вопрос – можно ли применять не только на картинках но и на ?”
      Меня этот вопрос также интересует.

    30. Сергей Чикуенок
      31 марта 2010

      Пока нельзя. Я думаю, как можно решить эту проблему

    31. Сергей Чикуенок
      14 апреля 2010

      Inqy, нельзя. SVG может маскировать только изображения, для HTML можно попробовать применить фильтры, но сомневаюсь, что результат вас устроит.

    32. ES
      19 мая 2010

      Всё… мой мозг лопнул.

      Серёжа, объясните пожалуйста, как сделать так, что-бы слон оживал при наведении мышки, а отмирал при её уборе?

    33. Сергей Чикуенок
      20 мая 2010

      Посмотрите файл prites-article.js, там на строке 15 есть запуск анимации по нажатию на кнопку. Просто переделайте на hover()

    34. ES
      20 мая 2010

      Спасибо.

      Поменял. Теперь кнопку нажимать не надо, а достаточно курсором “притронуться” к ней. :) В общем с кривыми руками и абсолютным незнанием JS, проблема решаться – не решается… :)

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

      Серёжа, насколько я понял, в указанном вами файле инициализируются параметры анимированных спрайтов: высота, ширина всей картинки спрайта, “направление” анимации (указываются ручками), действие вызывающее скрипт анимации (который побороть мне не удалось: хотелось на слоника наводить, а получилось на кнопку) и скорость “живости” объекта, которая задаётся посредством другого скрипта (на фронте – ползунком).

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

      Навёл на слоника – побежал. Отвёл от слоника – замер. :) Спасибо.

    35. ES
      23 мая 2010

      Ура! Сделал. Правда без ховера… не осилил я его.

    36. Irina
      27 октября 2010

      Очень доходчиво! Спасибо)

    37. Юрий
      3 декабря 2010

      Сергей, доброго времени суток.
      Мне очень понравилась работа вашей библиотеки ictinus, и я хотел бы пользоваться ей при необходимости, но у меня возникла та же проблема, что и у Алексея. В ИЕ не удается назначить маску более чем одному изображению, а обработке подвергалась только последняя картинка, но при этом результат оказывался на месте первого изображения, назначенного маскировке. В качестве эксперимента пробовал назначать одинаковый id, разные id, назначал обработку всем тегам img внутри контейнера с указанным id.
      К сожалению нужного результата добиться не удалось. Подскажите решение проблемы

    38. Inqy
      3 декабря 2010

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

    39. Юрий
      3 декабря 2010

      А Вам вообще удалось решить эту проблему?

    40. 2 июля 2011

      Спасибо, понравился метод анимации спрайтами, да и про полупрозрачную окантовку отдельным изображением не догадывался раньше. А что насчёт требовательности к ресурсам аналогичного прелоудера, реализованного при помощи Canvas?

    41. Иван
      3 ноября 2011

      Сергей, ответь пожалуйста на вопрос.
      Вот я начинающий вебмастер, сделал меню полностью на спрайтах. Пункт меню – просто картинка, без шрифта. Все красиво, работает. Только вот, когда отключены картинки в браузере – ничего не видно, просто пусто. А если делать не спрайтами, а отдельными рисунками – тогда можно будет вывести текст в атрибуте alt рисунка..
      И вот у меня диллема, как же ЛУЧЩЕ поступить В ДАННОЙ ситуации. Спрайты – вроде модно, круто, скорость загрузки, css-rollover.. но очень плохо, когда отключены рисунки.
      Меню на картинках – лишние запросы к серверу, java-script rollover, нужно делать прелодер.. Но когда отключены рисунки – сайт сохраняет работоспособнось, название меню выводится через атрибут alt.
      Прокомментируй, если не затруднит )
      Спасибо!

    42. Inqy
      3 ноября 2011

      Иван, делается так – прозрачный пнг 1 на 1 с прописанными на растяжку размерами и альтернативным текстом, и ему в фон загоняется нужная часть спрайта. Так сказать, совмещаем приятное с полезным.

    43. Иван
      3 ноября 2011

      Спасибо большое!! Долго ломал голову над этим вопросом! )

    44. 9 декабря 2011

      Спасибо за ваши труды! Хорошая статья, как и все пожалуй. Сергей, не по теме немного, но увидел баг в тексте) – Хороший пример использования использования

    Комментировать

    Powered by Zen Coding

    (Spamcheck Enabled)