Сначала я попробовал воспользоваться техникой спрайтов, просто использовав вместо растра вектор, но тут возникло две проблемы:
- Масштабирование. При произвольных размерах элементов спрайта для того, чтобы его точно отпозиционировать, приходится танцевать с бубном. И при изменении размера самого спрайта (например, при добавлении нового элемента) все может поползти.
- Возможность стилизации посредством CSS отсутствует, потому что физически SVG-картинки в HTML-коде нет.
Тут я вспомнил про интеграцию картинок в CSS посредством кодирования оных base64 и использования data-uri. А с SVG все гораздо проще.Поскольку SVG-изображение — это XML-файл, то для него даже base64 не требуется, достаточно указать mime-тип как data:image/svg+xml и вставить содержимое SVG-файла одной строкой.
Возьмем, к примеру, иконку корзины:
Ее код выглядит примерно так:
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" version="1.1" viewBox="0 0 1850 1635" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="icon__cart">
<path fill="#101010" d="M1254 1163l-1203 0c-26,0 -51,-25 -51,-51l0 -131c0,-23 22,-51 51,-51l1075 0c22,0 36,-13 44,-30l96 -192c4,-9 1,-12 -8,-12l-1207 0c-22,0 -51,-25 -51,-51l0 -131c0,-23 23,-51 51,-51l1298 0c27,0 46,-13 56,-33l200 -401c9,-18 20,-29 41,-29l182 0c19,0 29,7 18,29l-554 1111c-5,10 -19,23 -38,23z"/>
<circle fill="#101010" cx="207" cy="1453" r="181"/>
<circle fill="#101010" cx="1062" cy="1453" r="181"/>
</g>
</svg>
Соответственно, использование ее в качестве фона в data-uri будет выглядеть так:
.element_whis_svg_background {
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" version="1.1" viewBox="0 0 1850 1635" xmlns:xlink="http://www.w3.org/1999/xlink">...</svg>');
}
Пробуем. В Хроме и Опере все замечательно, а вот в IE9-10-11, Safari, и, как ни странно, Firefox — фоновая картинка не отображается, они не понимают такой формат записи. Лезем искать, в чем проблема и выясняем, что для IE и прочих для содержимого SVG-файла надо выполнить URI-кодирование строки по стандарту RFC 3986. Кодируем, получаем вот такую конструкцию:
%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201850%201635%22%3E%3Cg%20fill%3D%22%23101010%22%3E%3Cpath%20d%3D%22M1254%201163H51c-26%200-51-25-51-51V981c0-23%2022-51%2051-51h1075c22%200%2036-13%2044-30l96-192c4-9%201-12-8-12H51c-22%200-51-25-51-51V514c0-23%2023-51%2051-51h1298c27%200%2046-13%2056-33l200-401c9-18%2020-29%2041-29h182c19%200%2029%207%2018%2029l-554%201111c-5%2010-19%2023-38%2023z%22%2F%3E%3Ccircle%20cx%3D%22207%22%20cy%3D%221453%22%20r%3D%22181%22%2F%3E%3Ccircle%20cx%3D%221062%22%20cy%3D%221453%22%20r%3D%22181%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E
Подставляем ее в background, проверяем:
.element_whis_svg_background {
background: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2 ... svg%3E');
}
Ура, все работает! Нюанс — кодировку в данном случае указывать не нужно. Если указать — то перестанет работать в IE.
Ну и, наконец, для того, чтобы внешний вид иконки можно было настраивать, используем любой препроцессор (я использую LESS, но это не принципиально) и оформим наш класс иконки как примесь (mixin). Для того, чтобы можно было менять цвет заливки, находим в кодированной строке атрибут fill, там будет что-то типа:
fill%3D%22%23101010%22
То, что находится между %23 и %22 — и есть искомый код цвета, на место которого надо подставить переменную. В итоге получим следующее:
.icon__cart(@width, @height, @fill) {
content: '';
display: block;
width: @width;
height: @height;
background-size: contain;
background-repeat: no-repeat;
background: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201850%201635%22%3E%3Cg%20fill%3D%22%23@{fill}%22%3E%3Cpath%20d%3D%22M1254%201163H51c-26%200-51-25-51-51V981c0-23%2022-51%2051-51h1075c22%200%2036-13%2044-30l96-192c4-9%201-12-8-12H51c-22%200-51-25-51-51V514c0-23%2023-51%2051-51h1298c27%200%2046-13%2056-33l200-401c9-18%2020-29%2041-29h182c19%200%2029%207%2018%2029l-554%201111c-5%2010-19%2023-38%2023z%22%2F%3E%3Ccircle%20cx%3D%22207%22%20cy%3D%221453%22%20r%3D%22181%22%2F%3E%3Ccircle%20cx%3D%221062%22%20cy%3D%221453%22%20r%3D%22181%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E');
}
Используется данная примесь следующим образом (образец произвольный):
.block-whis-icon._cart {
...;
&::before {
.icon__cart(30px, 20px, '1d9fbb');
...;
}
}
Вот, собственно, и все. LESS- (SASS, Stylus, etc.) файлик с такими примесями отлично заменяет спрайт.
Для оптимизации и кодирования SVG я использовал GULP, плагины gulp-svgo и gulp-data-uri-stream. Да, этот метод дает возможность менять лишь размеры иконки, цвет заливки и обводки, но в большинстве своем этого вполне достаточно.
Комментарии (23)
bolk
12.05.2015 19:47+4Что-то я не понял о чём статья. Ну да, есть SVG и да, её можно вставить в data URI. Всё?
Почитайте лучше вот этот блог: css.yoksel.ru или этот: pepelsbey.net там тоже про SVG, но глубоко и интересно.Fayon Автор
12.05.2015 22:18Я столкнулся с конкретной проблемой — манипуляции цветом иконок, заданых через background-image. Для того, чтобы ее решить, мне пришлось активно покопаться в гугле. Вот для тех, кто столкнется с подобной проблемой или просто только начинает осваивать svg я и написал свой вариант решения. Если у вас есть лучше — я с удовольствием вас выслушаю.
bolk
12.05.2015 22:21Да пожалуйста: не ставить иконку фоном, а сделать обычной картинкой, цветом манипулировать через CSS.
Fayon Автор
12.05.2015 22:32А если этот вариант мне не подходит? Вот так получилась, что у меня примерно 80% иконок на сайте сделаны через фон, спрайтом. Вы мне предлагаете перепиливать шаблоны только для того, чтобы заменить png-спрайт на svg, если я могу просто поменять background в less?
Плюс если иконки вставлять обычной картинкой — то каждая картинка это запрос к серверу. А, наскольк я помню, технология спрайтов была создана как раз для минимизации оных запросов. А мой вариант — это как раз замена спрайту.bolk
13.05.2015 07:10+1Хосподи, чего там «перепиливать»-то? А технология спрайтов и png здесь каким боком? Мы, вроде, SVG обсуждаем. Хотя я и не понял вопроса, всё же отвечаю: загрузите SVG одним файлом, а потом используйте тег USE.
Fayon Автор
13.05.2015 09:19Хосподи, чего там «перепиливать»-то?
Да, действительно, чего там перепиливать… Всего-то пару сотен иконок найти в шаблонах, повставлять инлайново, поправить цсс для каждого элемента. При том, что можно обойтись просто последним пунктом.bolk
13.05.2015 09:28+1Вам же известно что на что менять, да? И вы программист?
Я неоднократно был в ситуации «пара сотен замен по известному алгоритму», ну и что? И ещё буду в таких ситуациях. Вы лучше не распаляйтесь, а прислушайтесь.
artemmalko
13.05.2015 10:47Возможно вам стоит пересмотреть подход, так как svg-изображения в данном случае будут рендериться медленнее (настолько, что это становится заметно при кол-ве картинок > 50), чем использование стека svg (symbols) или инлайнового svg. И то, и то легко сделать плагинами для gulp.
ZimM
13.05.2015 12:20Получается, svg дублируется в результирующем css столько раз, сколько используется миксин? Неэффективненько.
antirek
13.05.2015 13:31Недавно сделал точно такую же штуку: заюзал svg чтобы цвет картинки в тон надписи менять.
github.com/antirek/callButton.js/blob/master/callButton.js#L169
тестировал правда сначала только под хром, выкатил на несколько сайтов, а потом пользователи firefox сообщили о досадном косяке, пришлось разобраться с кодированием.
bubuger
А не проще в Base64 svg загнать, раз уж конвертите файл? Да и занимать он будет как правило меньше в байтах.
Fayon Автор
Нет. Вся эта свистопляска задумывалась для возможности менять цвет иконки по необходимости. В base64 переменную цвета не подставишь.
bubuger
а что мешает в svg указать цвет через переменную currentColor fill=«currentColor» и менять дальше по мере вызова svg в css через атрибут color?
Fayon Автор
Это не работает в случае, когда svg используется как фоновая картинка.
mkuzmin
Можно еще post-css использовать. Написать плагин, который делает URI-кодирование для svg строк в background.
Fayon Автор
Увы, с post-css я не работал. Впрочем, можно и для gulp плагин написать, но, увы, я не настолько хорошо знаю ноду, чтобы плагин не только делал URI-кодирование, но и вставлял переменные в нужные места((
mkuzmin
вот нашел плагин для post-css, может поможет github.com/borodean/postcss-assets#inlining-files