Как я использую SVG-спрайты

от автора

Привет, разработчик!
При верстке макета из PSD часто иконки вставлены в формате SVG. А если нет — прошу их у дизайнера. Ранее я использовал иконочные шрифты, но недавно увидел преимущества спрайтов и решил попробовать с ними поиграться внедрить их в процесс разработки. Мне нравятся иконочные шрифты, но они имеют ряд недостатков(на эту тему почитайте CSSTricks). Эксперимент удался, и вот как я организовал систему.

Условия

Что я хочу получить от спрайтов:

  1. Гибкое управление размером, цветом и поведением(hover, focus etc) иконки
  2. Автоматизация, минимум ручной работы
  3. Кеширование иконок для хорошей скорости загрузки страниц
  4. Удобная вставка иконок в разметку страницы (для шаблонизации 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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *