Привет, разработчик!
При верстке макета из PSD часто иконки вставлены в формате SVG. А если нет — прошу их у дизайнера. Ранее я использовал иконочные шрифты, но недавно увидел преимущества спрайтов и решил попробовать с ними поиграться внедрить их в процесс разработки. Мне нравятся иконочные шрифты, но они имеют ряд недостатков(на эту тему почитайте CSSTricks). Эксперимент удался, и вот как я организовал систему.
Условия
Что я хочу получить от спрайтов:
- Гибкое управление размером, цветом и поведением(hover, focus etc) иконки
- Автоматизация, минимум ручной работы
- Кеширование иконок для хорошей скорости загрузки страниц
- Удобная вставка иконок в разметку страницы (для шаблонизации html я использую jade)
Моя структура папок:
├── gulpfile.js # gulpfile └──assets # здесь редактируем файлы └── jade/ # шаблонизатор html └── sass/ # стили └── js/ # скрипты └── i/ # картинки, сюда мы и будем вставлять спрайт └──dist # здесь получаем готовый проект
Подробнее о том как работает моя сборка — можете почитать в репозитории.
Для создания спрайта используем gulp, а именно:
- gulp-svg-sprites — создание спрайта
- gulp-svgmin — минификация SVG
- gulp-cheerio — удаление лишних атрибутов из svg
- gulp-replace — фиксинг некоторых багов, об этом ниже
Поехали!
Устанавливаем плагины(я это делаю глобально и потом линкую):
npm install gulp-svg-sprites gulp-svgmin gulp-cheerio gulp-replace -g npm link gulp-svg-sprites gulp-svgmin gulp-cheerio gulp-replace
В gulpfile объявляем плагины:
var svgSprite = require('gulp-svg-sprites'), svgmin = require('gulp-svgmin'), cheerio = require('gulp-cheerio'), replace = require('gulp-replace');
Варим спрайт
Первый таск — создаем html-файл с тегами symbol.
gulp.task('svgSpriteBuild', function () { return gulp.src(assetsDir + 'i/icons/*.svg') // minify svg .pipe(svgmin({ js2svg: { pretty: true } })) // remove all fill and style declarations in out shapes .pipe(cheerio({ run: function ($) { $('[fill]').removeAttr('fill'); $('[style]').removeAttr('style'); }, parserOptions: { xmlMode: true } })) // cheerio plugin create unnecessary string '>', so replace .pipe(replace('&g t;', '>')) // remove space between 'g' and 't' letters // build svg sprite .pipe(svgSprite({ mode: "symbols", preview: false, selector: "icon-%f", svg: { symbols: 'symbol_sprite.html' } } )) .pipe(gulp.dest(assetsDir + 'i/')); });)); });
Давайте разберемся, что тут происходит по частям.
Говорим откуда нам нужно взять иконки и минифицируем их. Переменная assetsDir — для удобства.
return gulp.src(assetsDir + 'i/icons/*.svg') // minify svg .pipe(svgmin({ js2svg: { pretty: true } }))
Удаляем атрибуты style и fill из иконок, для того чтобы они не перебивали стили, заданные через css.
.pipe(cheerio({ run: function ($) { $('[fill]').removeAttr('fill'); $('[style]').removeAttr('style'); }, parserOptions: { xmlMode: true } }))
Но я заметил у данного плагина один баг — иногда он преобразовывает символ ‘>’ в кодировку ‘&g t;’, только без пробела между символами ‘g’ и ‘t'(если убрать пробел — получится символ).
Эту проблему решает следующий кусок таска:
.pipe(replace('&g t;', '>')) // remove space between 'g' and 't' letters
Теперь сделаем из получившегося спрайт и положим в папку:
.pipe(svgSprite({ mode: "symbols", preview: false, selector: "icon-%f", svg: { symbols: 'symbol_sprite.html' } } )) .pipe(gulp.dest(assetsDir + 'i/'));
symbol_sprite.html — и есть наш спрайт. Внутри он будет содержать следующее(для простоты у меня пара иконок):
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="0" height="0" style="position:absolute"> <symbol id="icon-burger" viewBox="0 0 66 64"> <path fill-rule="evenodd" clip-rule="evenodd" d="M0 0h66v9H0V0zm0 27h66v9H0v-9zm0 27h66v9H0v-9z"/> </symbol> <symbol id="icon-check_round" viewBox="-0.501 -0.752 18 18"> <path d="M8.355 0C3.748 0 0 3.748 0 8.355s3.748 8.355 8.355 8.355 8.355-3.748 8.355-8.355S12.962 0 8.355 0zm0 15.363c-3.865 0-7.01-3.144-7.01-7.01 0-3.864 3.145-7.007 7.01-7.007s7.01 3.144 7.01 7.01-3.146 7.007-7.01 7.007z"/> <path d="M11.018 5.69l-3.9 3.9L5.69 8.165c-.262-.263-.688-.263-.95 0-.264.263-.264.69 0 .952l1.9 1.903c.132.13.304.196.476.196s.344-.066.476-.197l4.376-4.378c.263-.263.263-.69 0-.952s-.69-.262-.952 0z"/> </symbol> </svg>
Щепотка стилей
Теперь нам нужно сделать стили для нашего спрайта(в данном случае файл .scss). В плагине gulp-svg-sprites мы задать этот файл, но вот какая досада — его нельзя сделать при данной настройке:
mode: "symbols"
Я решил сделать для создания scss отдельный таск. Если вы нашли другое решение, напишите в комментариях.
// create sass file for our sprite gulp.task('svgSpriteSass', function () { return gulp.src(assetsDir + 'i/icons/*.svg') .pipe(svgSprite({ preview: false, selector: "icon-%f", svg: { sprite: 'svg_sprite.html' }, cssFile: '../sass/_svg_sprite.scss', templates: { css: require("fs").readFileSync(assetsDir + 'sass/_sprite-template.scss', "utf-8") } } )) .pipe(gulp.dest(assetsDir + 'i/')); });
В свойстве cssFile объявляем, куда положить на scss файл(потом инклудим его).
В свойстве templates объявляем, где взять для него шаблон. Код моего шаблона:
.icon { display: inline-block; height: 1em; width: 1em; fill: inherit; stroke: inherit; } {#svg} .{name} { font-size:{height}px; width:({width}/{height})+em; } {/svg}
Получаем _svg_sprite.scss следующего содержания:
.icon { display: inline-block; height: 1em; width: 1em; fill: inherit; stroke: inherit; } .icon-burger { font-size:64px; width:(66/64)+em; } .icon-check_round { font-size:18px; width:(18/18)+em; }
Скомпилированный css будет таким:
.icon { display: inline-block; height: 1em; width: 1em; fill: inherit; stroke: inherit; } .icon-burger { font-size: 64px; width: 1.03125em; } .icon-check_round { font-size: 18px; width: 1em; }
Обратите внимание, что размеры иконок выражены через em, что позволит нам в дальнейшем управлять ими через font-size.
Составляем итоговый таск, чтобы запускать одну команду:
gulp.task('svgSprite', ['svgSpriteBuild', 'svgSpriteSass']);
Подключаем на страницу
Итак мы получили html-файл с иконками и scss-файл с оформлением. Далее подключим файл на страницу, используя кеширование через localStorage. Этот процесс подробно описан в статье Caching SVG Sprite in localStorage.
Подключаем js-файл следующего содержания:
;( function( window, document ) { 'use strict'; var file = 'i/symbol_sprite.html', revision = 1; if( !document.createElementNS || !document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' ).createSVGRect ) return true; var isLocalStorage = 'localStorage' in window && window[ 'localStorage' ] !== null, request, data, insertIT = function() { document.body.insertAdjacentHTML( 'afterbegin', data ); }, insert = function() { if( document.body ) insertIT(); else document.addEventListener( 'DOMContentLoaded', insertIT ); }; if( isLocalStorage && localStorage.getItem( 'inlineSVGrev' ) == revision ) { data = localStorage.getItem( 'inlineSVGdata' ); if( data ) { insert(); return true; } } try { request = new XMLHttpRequest(); request.open( 'GET', file, true ); request.onload = function() { if( request.status >= 200 && request.status < 400 ) { data = request.responseText; insert(); if( isLocalStorage ) { localStorage.setItem( 'inlineSVGdata', data ); localStorage.setItem( 'inlineSVGrev', revision ); } } } request.send(); } catch( e ){} }( window, document ) );
Все, мы подключили наш файл на страницу, после первой загрузки он кешируется.
Иконки я встраиваю через миксин jade, т.к. это быстро и удобно:
mixin icon(name,mod) - mod = mod || '' svg(class="icon icon-" + name + ' ' + mod) use(xlink:href="#icon-" + name)
Теперь, чтобы встроить иконку вызываем миксин с её именем:
+icon('check_round','red_mod') +icon('burger','green_mod')
Результирующий html:
<svg class="icon icon-check_round red_mod"> <use xlink:href="#icon-check_round"></use> </svg> <svg class="icon icon-burger green_mod"> <use xlink:href="#icon-burger"></use> </svg>
Открываем страницу в браузере:

Пока размеры иконок в натуральную величину и имеют стандартный цвет. Изменим это(не в сгенерированном файле, а в главном):
.icon-burger { font-size:3rem; &.green_mod { fill:green; } } .icon-check_round { font-size:3rem; &.red_mod { fill: red; } }
Результат:

Вот и все, мы получили рабочую систему подключения иконок через спрайты, но есть еще один момент.
Размытие
К сожалению, не все дизайнеры делают иконки по пиксельной сетке. В этом случае иконки будут «размываться». Если вы экспортируете иконки из иллюстратора вам нужно включить пиксельную сетку и подогнать размер и расположение иконки под пиксельную сетку. Если вы работаете в готовыми svg-файлами — воспользуйтесь сервисом iconmoon для их правильного выравнивания.
Особую благодарность выражаю @akella, который помог мне в написании данного решения.
ссылка на оригинал статьи http://habrahabr.ru/post/272505/
Добавить комментарий