Спрайты в stylus


Ты амбициозный веб-разработчик. Почитываешь на досуге phptime.ru, чтобы быть в курсе всех трендов. С особым интересом наблюдаешь за блогом про frontend. Прочитал там пост про css-препроцессор stylus и решил его использовать на своих проектах. Быстро разобрался что к чему, написал парочку миксинов. Экономишь свое время, все супер. Но тут вдруг возникла необходимость использовать спрайты. «Неплохо бы генерировать их на лету», — думаешь ты. Лезешь в документацию стилуса, судорожно листаешь страницу за страницей и ничего не находишь… Да, можно воспользоваться конвертацией в data-uri, stylus это умеет, но хочется всё же теплых и ламповых спрайтов. Еще и хипстеры-рубисты набежали из соседнего отдела, хвастаются своим Sass, издеваются. Хватит это терпеть!


Обзор решений начнем с самого простого — Stylus-Sprite.
Устанавливается это расширение для стилуса легко:

npm install stylus-sprite
Но предварительно нужно еще установить графические библиотеки libgd и node-gd. Если у тебя linux, то проблем возникнуть не должно. Пользователям windows и mac os(если нет homebrew) скорее всего придется повозиться с libgd, но в интернетах можно найти подробные мануалы, так что не отчаивайся.
Для генерации спрайтов с помощью Stylus-Sprite в styl-файлах вместо background-image необходимо указывать определенный при подключении файл со спрайтами(в примере это sprite.png), а вместо background-position вызывать функцию sprite, в аргументах которой передается файл с исходным изображением:
background: url(sprite.png) no-repeat sprite("auth-ico.png")

В скомпилированном css мы увидим такой результат:
background: url(sprite.png) no-repeat -100px -50px;
Файл sprite.png будет содержать все использованные нами изображения. Чудеса! Кроме того, в функцию sprite можно передавать дополнительные параметры для ресайза, сдвига, повтора и некоторых других операций над обрабатываемыми картинками.
Для подключения Stylus-Sprite необходимо пользоваться его JavaScript API. Выглядит это примерно следующим образом:
var stylus = require("stylus"),
    StylusSprite = require("stylus-sprite")
    sprite = new StylusSprite({output_file:"sprite.png"});

var stylusSrc = "#YOUR STYLUS CODE HERE#";

stylus(stylusSrc).
    set('filename', 'styles.css').
    define('sprite', function(filename, option_val){
        return sprite.spritefunc(filename, option_val);
    }).
    render(function(err, stylusSrc){
        if (err) throw err;
        sprite.build(stylusSrc, function(err, stylusSrc){
            if (err) throw err;
            console.log(stylusSrc);
        });
    });

Удобнее всего, мне показалось, внедрить это расширение в задачу grunt.js для компиляции css из styl, заодно можно вынести все настройки Stylus Sprite в grunt-файл.
Теперь о минусах. Во-первых, катастрофически неудобно использовать это решение, когда нужно генерировать разные наборы изображений в отдельные файлы. Во-вторых, невозможно использовать Stylus Sprite, если пользуешься консольным приложением стилуса. В-третьих, расширение на данный момент является deprecated, поэтому о саппорте и появлении новых фич остается только мечтать.

Перейдем к следующему решению — Stylus-Lemonade. Кстати, именно в пользу него прекратилась разработка Stylus Sprite. Stylus-Lemonade является полноценным плагином для стилуса и обладает более гибким функционалом. Он способен генерировать любое количество файлов из всевозможных наборов картинок.
Работа с этим плагином сводится к использованию 4 функций при написании кода в styl-файлах: sprite-position(), sprite-height(), image-width(), sprite-map(). Приведу пример из документации:
$animated_flame = sprite-map('flame')
#flame
  background: url(sprite-url($animated_flame)) no-repeat
  height: sprite-height($animated_flame, 'flame_a_0001')
  width: sprite-width($animated_flame, 'flame_a_0001')
.flame-frame-1
  background-position: sprite-position($animated_flame, 'flame_a_0001') !important
.flame-frame-2
  background-position: sprite-position($animated_flame, 'flame_a_0002') !important
.flame-frame-3
  background-position: sprite-position($animated_flame, 'flame_a_0003') !important
Для подключения Stylus-Lemonade достаточно воспользоваться методом стилуса use():
var stylus = require('stylus');
stylus(markup_input)
  .use(require('stylus-lemonade')())
  .render(function(err, css_output){
    console.log(css_output);
  });

Для работы также требуется графическая библиотека libgd.
В целом, решение неплохое, однако есть пара ложек дегтя: 1) не работает с обычным стилусом, необходимо устанавливать форк от разработчика плагина(отличается от оригинала всего на 2 строчки); 2) с недавних пор плагин стал deprecated. автор ударился в какой-то эзотерический CoffeeStylesheet и объявил, что новых функций для Stylus-Lemonade не планируется.

Последнее в данном обзоре решение для автоматической генерации спрайтов называется grunt-spritesmith, и нельзя сказать, что оно имеет прямое отношения к стилусу, так как по сути является задачей для grunt.js. В основе лежит одноименный продукт Spritesmith.
Для установки выполни в консоли
npm install grunt-spritesmith

Затем добавь в свой grunt-файл проекта строчку
grunt.loadNpmTasks('grunt-spritesmith');

и останется только настроить конфиг данной задачи, что не составит для тебя труда, если прочитаешь предыдущий пост в этом блоге.
Grunt-spritesmith является мульти-таском, следовательно, позволяет генерировать неограниченное количество спрайтов. На выходе можно получить файл в форматах stylus, scss, sass, less, json, который будет содержать переменные с параметрами каждого изображения из набора, а также функции для их удобного использования. Вот небольшой пример:
$sprite1_x = 0px;
  $sprite1_y = 0px;
  $sprite1_offset_x = 0px;
  $sprite1_offset_y = 0px;
  $sprite1_width = 50px;
  $sprite1_height = 50px;
  $sprite1 = 0px 0px 0px 0px 50px 50px;
  
  spriteX($sprite) {
    return $sprite[0];
  }

  spriteY($sprite) {
    return $sprite[1];
  }

  spriteOffsetX($sprite) {
    return $sprite[2];
  }

  spriteOffsetY($sprite) {
    return $sprite[3];
  }

  spriteWidth($sprite) {
    return $sprite[4];
  }

  spriteHeight($sprite) {
    return $sprite[5];
  }

  spriteBackground() {
      return 'sprite.png';
  }

  sprite($sprite) {
    background-image: url(spriteBackground());
    background-position: spriteOffsetX($sprite) spriteOffsetY($sprite);
    width: spriteWidth($sprite);
    height: spriteHeight($sprite);
  }

Изображение на выходе может быть в формате jpg или png, можно также указать алгоритм склеивания и движок для работы с картинками. Подход у этого решения непростой, но с нашей задачей оно справляется, кроме того, является довольно гибким

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