Векторная замена спрайтам — имплантируем SVG в CSS

Не так давно компания, в которой я работаю, наконец-то окончательно отказалась от поддержки IE8 и, как следствие, я вплотную занялся вопросом перехода с растровых иконок на векторные. Основные плюсы SVG — это масштабирование, малый вес и возможность стилизации через CSS.

Сначала я попробовал воспользоваться техникой спрайтов, просто использовав вместо растра вектор, но тут возникло две проблемы:
  • Масштабирование. При произвольных размерах элементов спрайта для того, чтобы его точно отпозиционировать, приходится танцевать с бубном. И при изменении размера самого спрайта (например, при добавлении нового элемента) все может поползти.
  • Возможность стилизации посредством CSS отсутствует, потому что физически SVG-картинки в HTML-коде нет.


Тут я вспомнил про интеграцию картинок в CSS посредством кодирования оных base64 и использования data-uri. А с SVG все гораздо проще.Поскольку SVG-изображение — это XML-файл, то для него даже base64 не требуется, достаточно указать mime-тип как data:image/svg+xml и вставить содержимое SVG-файла одной строкой.

Возьмем, к примеру, иконку корзины:

image

Ее код выглядит примерно так:

<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. Да, этот метод дает возможность менять лишь размеры иконки, цвет заливки и обводки, но в большинстве своем этого вполне достаточно.
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 27

    –2
    А не проще в Base64 svg загнать, раз уж конвертите файл? Да и занимать он будет как правило меньше в байтах.
      +1
      Нет. Вся эта свистопляска задумывалась для возможности менять цвет иконки по необходимости. В base64 переменную цвета не подставишь.
        –2
        а что мешает в svg указать цвет через переменную currentColor fill=«currentColor» и менять дальше по мере вызова svg в css через атрибут color?
          0
          Это не работает в случае, когда svg используется как фоновая картинка.
        +2
        Можно еще post-css использовать. Написать плагин, который делает URI-кодирование для svg строк в background.
          0
          Увы, с post-css я не работал. Впрочем, можно и для gulp плагин написать, но, увы, я не настолько хорошо знаю ноду, чтобы плагин не только делал URI-кодирование, но и вставлял переменные в нужные места((
      +4
      Что-то я не понял о чём статья. Ну да, есть SVG и да, её можно вставить в data URI. Всё?

      Почитайте лучше вот этот блог: css.yoksel.ru или этот: pepelsbey.net там тоже про SVG, но глубоко и интересно.
        0
        Я столкнулся с конкретной проблемой — манипуляции цветом иконок, заданых через background-image. Для того, чтобы ее решить, мне пришлось активно покопаться в гугле. Вот для тех, кто столкнется с подобной проблемой или просто только начинает осваивать svg я и написал свой вариант решения. Если у вас есть лучше — я с удовольствием вас выслушаю.
          0
          Да пожалуйста: не ставить иконку фоном, а сделать обычной картинкой, цветом манипулировать через CSS.
            0
            А если этот вариант мне не подходит? Вот так получилась, что у меня примерно 80% иконок на сайте сделаны через фон, спрайтом. Вы мне предлагаете перепиливать шаблоны только для того, чтобы заменить png-спрайт на svg, если я могу просто поменять background в less?
            Плюс если иконки вставлять обычной картинкой — то каждая картинка это запрос к серверу. А, наскольк я помню, технология спрайтов была создана как раз для минимизации оных запросов. А мой вариант — это как раз замена спрайту.
              +1
              Хосподи, чего там «перепиливать»-то? А технология спрайтов и png здесь каким боком? Мы, вроде, SVG обсуждаем. Хотя я и не понял вопроса, всё же отвечаю: загрузите SVG одним файлом, а потом используйте тег USE.
                0
                Хосподи, чего там «перепиливать»-то?

                Да, действительно, чего там перепиливать… Всего-то пару сотен иконок найти в шаблонах, повставлять инлайново, поправить цсс для каждого элемента. При том, что можно обойтись просто последним пунктом.
                  +1
                  Вам же известно что на что менять, да? И вы программист?

                  Я неоднократно был в ситуации «пара сотен замен по известному алгоритму», ну и что? И ещё буду в таких ситуациях. Вы лучше не распаляйтесь, а прислушайтесь.
        0
        Перенес комментарий в нужную ветку
          0
          Возможно вам стоит пересмотреть подход, так как svg-изображения в данном случае будут рендериться медленнее (настолько, что это становится заметно при кол-ве картинок > 50), чем использование стека svg (symbols) или инлайнового svg. И то, и то легко сделать плагинами для gulp.
            0
            С этим нюансом еще не сталкивался, проверю. Спасибо!
              +2
              Тут можно прочесть подробнее.
                0
                Благодарю!
              0
              С этим способом есть очень много проблем у Firefox (#1, #2, #3, список могу продолжать бесконечно). Некоторые баги висят уже почти 10 лет и не фиксятся. Так что инлайн SVG не такая и плохая идея.
                0
                Еще можно использовать svg-спрайт. С ним таких проблем вроде бы нет. Правда цвет уже не поменяешь.
                  0
                  SVG-спрайт вставленный через background-image? Тогда проблем в Firefox и правда нет нет, но появляются проблемы при зуме в Chrome :) И цвета не поменять, да.
                    0
                    padding между картинками в спрайте решает эту проблему)
              0
              Получается, svg дублируется в результирующем css столько раз, сколько используется миксин? Неэффективненько.
                0
                Не имеет значения, так как gzip.
                0
                Недавно сделал точно такую же штуку: заюзал svg чтобы цвет картинки в тон надписи менять.
                github.com/antirek/callButton.js/blob/master/callButton.js#L169

                тестировал правда сначала только под хром, выкатил на несколько сайтов, а потом пользователи firefox сообщили о досадном косяке, пришлось разобраться с кодированием.
                  –1
                  Вот сделано в css и все работает yabeda.by

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое