Создание SVG спрайтов с помощью Gulp и Sass
В этом посте пойдёт речь о процессах и используемых инструментах для создания SVG спрайтов в студии Liquid Light.
Создание и поддержка больших SVG спрайтов может быть сложным делом и времязатратным. Поэтому мы решили автоматизировать процесс. И вместо использования одного большого SVG спрайта и отслеживания координат каждой иконки индивидуально, мы бы хотели иметь возможность редактировать каждую иконку и создавать и размещать сгенерированные иконки автоматически.
На практике это значит, что нам нужно сложить все наши SVG иконки в папку и будет создан SVG спрайт (и PNG резерв для IE8) и оптимизирован автоматически вместе с Sass картой или именами и скоординирован. Используя Sass миксины, мы далее сможем включать наши спрайты используя маленькие куски кода:
button {
&:before {
@include sprite(search);
}
}
Весь код можно найти на Github
Автоматизация процесса
Для интеграции 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 и CSSgulp-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
12-12-2014 gulp.js Frontend Sass SVG Виктор Матушевский