В этом посте пойдёт речь о процессах и используемых инструментах для создания SVG спрайтов в студии Liquid Light.

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

На практике это значит, что нам нужно сложить все наши SVG иконки в папку и будет создан SVG спрайт (и PNG резерв для IE8) и оптимизирован автоматически вместе с Sass картой или именами и скоординирован. Используя Sass миксины, мы далее сможем включать наши спрайты используя маленькие куски кода:

button { &:before { @include sprite(search); } }

Весь код можно найти на Github

[[$googleAdsence]]

Автоматизация процесса

Для интеграции SVG спрайтов в наш рабочий процесс, мы решили, что хотим чтобы исполнитель задач создавал спрайт - это значит, что мы могли бы отдельно создавать и обновлять индивидуальные иконки без редактирования и обновления целого изображения. Gulp - это выбранный нами исполнитель задач, который кроме всего прочего может запускать gulp-svg-sprites. Также мы хотели, чтобы CSS создавались автоматически - с размерами и позициями фона, вычисляемые при создании. Это даёт нам преимущество в возможности определять размеры иконок и обновлять CSS, чтобы они это отображали.

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

При использовании Sass, иконки хранятся в массиве - или “карте” (подробнее про Sass карты). Используя некоторые миксины, мы можем вызывать любую иконку в спрайте и после компиляции, выводить размеры и позицию фона для каждой иконки.

Этот пост - это не вступление в Gulp или Sass (уже есть куча подобных вступлений в веб, можете почитать вот эту статью от Mark Goodyear, Sitepoint и Codefellows ), скорре этот пост описывает детально рабочий процесс для создания и использования SVG спрайтов. Он выполняется через плагины gulp, задачи gulp, которые мы установили и специфические миксины.

Плагины Gulp - package.json

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

{ "devDependencies": { "gulp": "^3.8.7", "gulp-load-plugins": "^0.4.0", "gulp-svg-sprites": "^1.0.0", "gulp-svg2png": "^0.3.0", "gulp-svgo": "^0.1.1", "gulp-util": "^2.2.14" } }

Пробежимся по каждому из используемых плагинов

  • gulp-load-plugins - см. ниже
  • gulp-svg-sprites - это тяжёлая артилерия, созадёт спрайты SVG и CSS
  • gulp-svg2png - преобразовывает SVG в PNG - будем использовать для создания нашего PNG резерва
  • gulp-svgo - SVG оптимизатор - для оптимизации наших SVG иконок перед тем, как мы создадим спрайт
  • gulp-util - используется для вывода цветных сообщений на экран

Плагин gulp-load-plugins (от Jack Franklin), который находится в package.json и загружает все плагины gulp как один большой проект - это значит, что вам не нужно указывать каждый из них в вашем gulp файле. Сэкономит чуть вам времени!

`var gulp = require(‘gulp’);

var gutil = require(‘gulp-util’); var plugins = require(“gulp-load-plugins”)({ pattern: [‘gulp-’, ‘gulp.’], replaceString: /\bgulp[-.]/ });`

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

.pipe(plugins.svg2png())

Gulp задача - gulpfile.js

У нас есть несколько gulp задач, чтобы сделать спрайт и соответствующие файлы (см. ниже). Первая задача svgSprite будет наблюдать за некоторой папкой; при добавлении или редактировании SVG файлов будет запускаться задача и будут созданы спрайт (с соответствующим scss файлом).

Отдельные SVG файлы проходят через оптимизатор SVG преед помещением в спрайт, чтобы обеспечить наименее возможный размер.

Далее, задача pngSprite конвертирует SVG в PNG спрайт для IE8 и ниже.

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

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

Внимание Убедитесь, что ваш спрайт не превышает размеры 2300px x 2300px - иначе <= iOS7 вообще не покажет изображение.

` gulp.task(‘svgSprite’, function () {

return gulp.src(paths.sprite.src)
    .pipe(plugins.svgo())
    .pipe(plugins.svgSprites({
        cssFile: paths.sprite.css,
        preview: false,
        layout: 'diagonal',
        padding: 5,
        svg: {
            sprite: paths.sprite.svg
        },
        templates: {
            css: require("fs").readFileSync(paths.templates.src + 'sprite-template.scss', "utf-8")
        }
    }))
    .pipe(gulp.dest(basePaths.dest));

});

gulp.task(‘pngSprite’, [‘svgSprite’], function() { return gulp.src(basePaths.dest + paths.sprite.svg) .pipe(plugins.svg2png()) .pipe(gulp.dest(paths.images.dest)); });

gulp.task(‘sprite’, [‘pngSprite’]);`

Scss шаблон - sprite-template.scss

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

Наш sprite-template.scss выглядит вот так (добавил пару строк для читаемости):

{#common} $icons: ( sprite: (width: {swidth}px, height: {sheight}px, pngPath: '{pngPath}', svgPath: '{svgPath}'), {/common} {#svg} {name}: (width: {width}px, height: {height}px, backgroundX: {positionX}px, backgroundY: {positionY}px), {/svg} {#common} ); {/common}

Блоки {common} сообщают генератору шаблонов, выводить эти блоки только раз. Первый объект открывает карту и вставляет данные о спрайте (ширину, высоту, путь к PNG и путь к SVG). Блок {common} в конце просто закрывает карту Sass.

Блок {svg} в середине проходит циклом через каждый файл и назначает имя файла, разрешение и положение фона.

Используя этот шаблон вместе с плагином gulp-svg-sprite, получим следующее:

`$icons: ( sprite: (width: 104px, height: 96px, pngPath: ‘../img/sprite.png’, svgPath: ‘../img/sprite.svg’),

facebook: (width: 10px, height: 22px, backgroundX: 0px, backgroundY: 0px),
twitter: (width: 32px, height: 22px, backgroundX: -20px, backgroundY: -32px),
twitterHover: (width: 32px, height: 22px, backgroundX: -62px, backgroundY: -64px),

);`

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

Scss миксины и функции

С сгенерированной картой, мы могли бы начать использовать значения нашего Scss файла следующим образом:

` @import “src/sprite”;

.class { $twitter: map-get($icons, twitter); $sprite: map-get($icons, sprite); width: map-get($twitter, width); height: map-get($twitter, height); background-image: url(map-get($sprite, svgPath)); background-position: map-get($twitter, backgroundX) map-get($twitter, backgroundX); }`

Вы поняли идею - это всё очень долго и нужно делать для каждой иконки, которую мы хотим использовать. Код выше не должен включать конвертирование px в em!

Чтобы избежать проблем, мы создали банк миксинов Scss, чтобы упростить использование спрайтов:

`@import ‘src/sprite’;

$ieSprite: ‘.lt-ie9’ !default; $sprite: map-get($icons, sprite) !default; $baseFontSize: 16px !default;

@import ‘mixins’;

.class { @include sprite(phone); }`

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

Так как наши сайты поддерживают IE8, поэтому PNG и px резервы очень важны.

Переменные

Нужно установить парочку переменных, чтобы потом было легче поддерживать:

$ieSprite: '.lt-ie9' !default; $sprite: map-get($icons, sprite) !default; $baseFontSize: 16px !default;

  • $ieSprite - объявляет класс необходимый для ie спрайта. При отсутствии не будет сгенерирован IE PNG резерв.
  • $sprite - устанавливает переменную для основных данных спрайта (путь файла, размеры и др.)
  • $baseFontSize - используется для вычислений mq-px2em

Функции

Далее, извлекаются функции и возвращают указанный атрибут для указанной иконки из sass карты. Мы также включили библиотеку от Guardian sass-mq library, что значит у нас есть функция mq-px2em.

`// Получение атрибутов из sass карты @function sprite-attr($icon, $attr) { $icon: map-get($icons, $icon); @return map-get($icon, $attr); }

@function icon-attr($icon) { $attr: ( width: sprite-attr($icon, width), height: sprite-attr($icon, height), x: sprite-attr($icon, backgroundX), y: sprite-attr($icon, backgroundY) );

@return $attr;

}`

Плейсхолдеры

Плейсхолдеры устанавливают фон как спрайт SVG или PNG спрайт, если класс для body содержит .lt-ie9

// Установка фона и размера с IE резервом %sprite { display: inline-block; background-image: url(map-get($sprite, svgPath)); background-size: mq-px2em(map-get($sprite, width)) mq-px2em(map-get($sprite, height)); } %ie-sprite { background-image: url(map-get($sprite, pngPath)); }

Миксины

Наконец у нас есть спрайт миксины. Есть миксин ie-sprite(), у которого схожый функционал, но он использует px вместо em и добавляет к селектору класс .lt-ie9.

`// Для использования с gulp спрайт плагином @mixin sprite($icon, $type: all) { @if $type == all { // Shares the backgrounds @extend %sprite; }

$iconMap: icon-attr($icon);

// Вывод размеров в em
@if $type == all or $type == size {
    width: mq-px2em(map-get($iconMap, width) + 1);
    height: mq-px2em(map-get($iconMap, height) + 1);
}

// Вывод положения фона в em
@if $type == all or $type == bg {
    background-position: mq-px2em(map-get($iconMap, x)) mq-px2em(map-get($iconMap, y));
}

// Добавление ie резерва
@include ie-sprite($icon, $type);

}`

Эти спрайт миксины берут 2 параметры - первый - это имя нужной иконки, второй - опциональный и позволяет пользователю определить размер size или фон bg для получения соответствующих атрибутов.

Использование

С установленным gulp процессом и при использовании миксинов, получить иконку для вывода очень просто:

`class { &:before { @include sprite(twitter); content: ‘’; float: left; margin-right: 0.5em; }

&:hover {
    &:before {
        @include sprite(twitterHover, bg);
    }
}

}`

Вывод CSS:

`.class:before { display: inline-block; background-image: url("../img/sprite.svg"); background-size: 6.5em 6em; }

.lt-ie9 .class:before { background-image: url("../img/sprite.png"); }

.class:before { width: 2.0625em; height: 1.4375em; background-position: -1.25em -2em; content: ‘’; float: left; margin-right: 0.5em; } .lt-ie9 .class:before { width: 32px; height: 22px; background-position: -20px -32px; } .class:hover:before { background-position: -3.875em -4em; } .lt-ie9 .class:hover:before { background-position: -62px -64px; }`

Хотя кажется, что много кода, на самом деле не более, чем бы у вас заняло написание всего CSS вручную. SVG устанавливается как фон для любой иконки использующей спрайт (только если у них нет lt-ie9 класса для body, в этом случае они получают PNG). С этого момента, положение фона, ширина и высота устанавливаются для каждого селектора отдельно - с px резервом для IE8 и ниже.

Весь приведенный код может быть найден на Github.

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

Источник - статья Creating SVG Sprites using Gulp and Sass