Спрайт

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

Спрайт (sprite) — это 2D-изображение, содержащее несколько других изображений. Спрайты достаточно часто используются в компьютерной графике, особенно при создании игр. Вот, например, спрайт с Мегамэном:

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

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

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

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

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

Тут на помощь и приходят спрайты.

 

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

Так выглядит иконочный спрайт на одном из наших проектов:

Как вывести спрайтовую иконку на страницу? Довольно просто:

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

Если еще раз взглянуть на изображение спрайта, можно заметить сетку (в нашем случае шаг сетки равен 30 пикселям).

Такая сетка, во-первых, дисциплинирует дизайнера — иконки из одного набора должны иметь одинаковый «визуальный вес». Помещая иконки в одинаковые «коробочки», добиться визуального соответствия легче.

Во-вторых, легче позиционировать иконки на странице относительно других элементов. Когда все иконки — одинаковые квадратики 30×30 пикселей, они будут смещать текст на одинаковое расстояние, и все будет стоять идеально ровно.

И в третьих, сетка помогает писать CSS-правила для иконок: не нужно измерять смещение фона в графическом редакторе, достаточно посчитать, сколько клеток сетки помещается между ним и краем спрайта. Посмотрите на иконку карандаша: его смещение легко посчитать — 7 клеток по горизонтали и 3 клетки по вертикали, то есть 210 (30×7) и 90 (30×3) пикселей, результирующий код: background-position: -210px -90px;

Обратите внимание на эту область:

Эта группа иконок меньше других, но стоят они по той же 30-пиксельной сетке, что и остальные. Зачем так? Чтобы облегчить дальнейшую поддержку спрайта. Если понадобится заменить иконки на другие, стандартного размера (или просто сделать их чуть больше), соседние иконки не придется смещать. И значение для backround-position считать проще.

Например, код для иконки маленькой телефонной трубки код будет таким:

Здесь дополнительно указан размер, так как он нестандартный.

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

CSS-правила превратятся в набор волшебных чисел, а редактирование спрайта — в долгие циклы отладки всех «поехавших» иконок.

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

В спрайтах это пустое место особенно важно: при масштабировании картинок (например, когда пользователь увеличивает страницу на экране смартфона) браузер использует фильтрацию для сглаживания результата, при этом по краям иконки могут «проявиться» пиксели от сглаживания соседних иконок. Вот, например, фрагмент меню сайта bike-centre.ru:

— И как вы предлагаете со всем этим работать? Изменилась картинка — пересобирать весь спрайт? Добавили новую иконку — снова пересобирать весь спрайт?

Конечно же, нет. Спрайт мы создаем на старте написания кода и храним его векторный исходник (это может быть .psd или .sketch, или что угодно) прямо в репозитории, чтобы любой разработчик мог добавить иконки, если возникнет необходимость. Контроль версий для спрайта тоже весьма полезен.

— Иконочные шрифты всегда идеального качества, потому что они векторные, а как ваши спрайты выглядят на ретине?

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

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

Здесь 420px и 210px – размеры обычного (1x) спрайта.

— Вы собираете спрайты руками? Есть же плагины для вебпака!

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

— Вы все еще используете PNG? SVG-иконки можно вставлять в текст кода страницы, никаких внешних файлов и дополнительных запросов к серверу. А еще перекрашивать стилями. Что на это скажете?

PNG тоже можно перекрашивать прямо из CSS, достаточно подключить спрайт в качестве mask-image, а не background-image (и, что очевидно, смещать с помощью mask-position вместо background-position). С инлайновыми SVG та же проблема, что и с автоматической сборкой спрайтов: нужно снова держать множество картинок в репозитории, чтобы отдавать их сборщику. Вдобавок, инлайновые картинки не кэшируются.

А еще можно собрать SVG-спрайт и использовать его.

— Спрайты — очень старая технология, почему вы пишете о них в 2018 году?

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