imageВ статье будет обсуждаться проблема вставки SVG иконок в веб — страницу.

Что мы имеем, и как мы используем это. Глобально есть три способа:

  • Вставка исходного кода SVG иконки прямо в DOM страницы.
  • С помощью HTML тега IMG
  • С помощью CSS background-image

Мы не будем говорить о варианте вставки иконок в виде шрифтов, ибо у этого подхода есть масса недостатков (некоторые из них чисто субъективны), о которых можно узнать с помощью поиска в Гугл, и вариант с IFRAME и OBJECT тегами тоже опустим по той же причине.

У всех этих способов есть свои достоинства и недостатки. С появлением SVG у нас появились новые требования к иконкам. Например, во времена PNG нам и в голову бы не пришло использовать одну и ту же иконку в разных размерах в разных частях сайта и с разными цветными заливками. Для этого нам пришлось бы попросить у дизайнера несколько иконок с разными цветами и размерами. Но с появлением SVG мы озверели и начали одну и ту же иконку вставлять повсюду, так как появилась возможность в соответствии с желаниями изменять вид иконки с помощью CSS.

Приведу пример:
Есть у нас иконка — звездочка для рейтингов. Где-то она белая, где-то желтая, большая и маленькая. Плюс может быть обрезанная половинка звездочки, скажем, для обозначения рейтинга 2.5 или 4+.

Для реализации данного примера нам нужно как минимум четыре PNG иконок для каждого цвета и формы, но если у нас SVG, то вместо четырех мы спокойно можем обойтись и одной единственной, специально оформленной для данной задачи и не только.

Нашу звездочку можно прямо вставить в DOM и с помощью стилей оформить ее так, как нам нужно. Это первый вариант, знакомая нам практика.

В первом варианте из достоинств можно перечислить удобное управление элементами графики, анимацию отдельных частей иконки и т.п., полное управление SVG тегами. Из недостатков первое, что в этом случае мы получаем большой размер DOM, что по некоторым причинам является не совсем хорошим феноменом. Вторым недостатком, по сравнению с третьим вариантом, является то, что размер, позиционирования (центровка по вертикали и горизонтали) является в некоторых случаях не тривиальной задачей.

А что насчет второго варианта, то он самый избыточный, так как не имеет ни одного из достоинств двух других вариантов.

Что у нас получается? Мы имеем два варианта вставки иконок, у которых есть свои достоинства и между которыми иногда бывает сложно сделать выбор.

А теперь представьте, что вы можете использовать почти все достоинства (кроме анимации отдельных частей иконки) первого варианта наряду с третьим вариантом.

То есть вставлять иконки с помощью CSS background-image, или HTML тега IMG, и по вашему усмотрению изменить цвет каждого тега в SVG, будь то path, polygon или circle, не имеет значения.

Вот как это будет выглядеть:

background-image: url(star.svg?p=red,$00ff00,,green);

или

<img src=”star.svg?p=red,$00ff00,,green”>

Думаю, что идея ясна, синтаксис уже зависит от настроек сервера, а над реализацией еще надо поразмышлять.

Тут ссылка того, как я решил для себя эту проблему. Приветствуются любые решения, если они крутые.

Как это работает


Структура директорий
  • caches — папка для кэша
  • colors — папка для хранения оригинальных цветов иконок
  • icons — папка для отформатированных иконок
  • originals — папка, куда скачиваются все иконки (ее можно задать и вручную)


Цикл работы
Берутся все иконки из папки originals, с помощью DOMDocument находятся все теги иконок, у которых есть цвет(fill), после цвет в иконке заменяется указателем на него в виде этого — "%1", а оригинальный цвет мы сохраняем в папке colors для случая, если мы захотим, чтобы она осталась неизменной.


Пример использования данной реализации.

icon/?p=parse /* запускает цикл работы */

icon/?p=empty /* удаляет все файлы, созданные вследствии работы команды parse */

background-image: url("icon/?p=leaderboard,red,blue,$000,green,orange,yellow,$0f0");

image

background-image: url("icon/?p=leaderboard,red,blue,$000");

image

background-image: url("icon/?p=leaderboard,,blue,$000,orange,red");

image

background-image: url("icon/?p=history");

image

background-image: url("icon/?p=history,$ff0000");

image

С точки зрения перформанса это не самая лучшая идея, даже если будет кэш, все равно это уже не будет выдача статика, там уже все равно будет работать скрипт, хотя можно использовать такую вставку только тогда, когда нужен другой цвет для иконки, но для больших проектов, где иконки становятся головной болью и в этом притоне не найдешь даже родного брата, то как по моим наблюдениям для таких проектов, пока что такой вариант может считаться — best practice.

P.S.: Пока что не успел на продакщене использовать, сижу гадаю, что можно еще подправить, или с какими проблемами можно столкнутся с этим, так что приветствуется любая критика, заранее спасибо.

P.S.S.: Дельный способ настройки сервера предлагает пользователь AlexLeonov habrahabr.ru/post/278825/#comment_8799315

Комментарии (20)


  1. xtech
    09.03.2016 12:10
    +7

    Мне кажется, что как раз производительность в данном случае будет играть большую роль, нежели какое-то удобство настройки иконок в проекте. Как никак +1 скрипт, который будет выполняться для каждой иконки против обычной отдачи статики nginx'ом и кэшированием самим браузером.

    Ваш подход конечно интересный, но как мне кажется не очень жизнеспособный.


    1. ckr
      10.03.2016 18:40

      Вы правы. На php в таком виде решение выглядит нелепо.
      Можно переконфигурировать так, чтобы nginx смотрел сначала есть ли файл в кэше, а уж потом при отсутствии оного вызывал какой-нибудь icon.php, который бы в свою очередь сгенерировал иконку и сохранил результат в кэш:

      <img src=”/icon/star-red-00ff000--green.svg”>

      Как по мне, для маленькой проблемы довольно сложный процесс развертки. Стоит учесть, что фронтендом иногда занимаются специалисты, не соображающие в php или настройку nginx. Или, даже соображающие, но просто не хотящие разворачивать этот ад в каждом проекте.

      На node.js решение выглядело бы интереснее. Держа шаблон svg-икноки в оперативной памяти, node выстреливала бы готовые файлы быстрее nginx, который каждый раз читает статику с диска; и плюс не нужно сохранять кэш, а в процессе разработки шаблона сайта (например, подборе цветов иконок, их толщин линий) кэш вообще не нужен. Оформить это как подключаемый middleware. Было бы интересно!


  1. psrafo
    09.03.2016 12:30
    -1

    Не для каждой иконки, а только тогда, когда нужно изменить цвет, хотя может быть так, что половина иконок именно будет работать через это. И да, это не очень круто, но если включить в дело Varnish + кеш браузера, то потери думаю, будут минимальными.


    1. xtech
      09.03.2016 13:26
      +1

      Понятное дело, что если все закэшировать, то и за производительностью можно не следить, но это же не совсем верный подход?


      1. psrafo
        09.03.2016 13:32

        Почему же не верный ?


        1. xtech
          09.03.2016 13:42

          Не буду утверждать, абсолютную верность утверждения, но:
          Кэширование — это самый последний механизм, который стоит применять, когда все другое уже перепробовано и дальше оптимизировать либо нечего, либо невозможно.


          1. psrafo
            09.03.2016 13:57

            Как я думаю, она верно для большинства, но не для всех случаях. А верно она в данном случае, думаю это уже на любителя.
            Я лично кэшов не стесняюсь, если они разумны.


  1. artemmalko
    09.03.2016 12:48

    А как же спрайт символов? А как же стек?
    При этом обоих этих подходах можно модифицировать цвета. + есть плагины для работы с этим через CLI или Gulp/Grunt/что-то еще.


    1. psrafo
      09.03.2016 13:01

      Про спрайт символов могу сказать, а как же старые версии платформ, которые его не поддерживают? А про стек впервые слышу, спасибо.


      1. artemmalko
        09.03.2016 13:15

        По умолчанию не все поддерживают спрайт символов из внешнего файла (легко полифилится, svg4everybody), а в остальном нет проблем. Вот стек да, не везде поддерживается без полифилов, но я его добавил для примера, так как это будущее SVG.


        1. psrafo
          09.03.2016 13:27

          В будущем да, наверное все будет через спрайн, но пока, есть люди, которые полифилов не используют, и на то есть свои причины, там нативность, простота, прозрачность и т.п.

          Да и будущее это не ясно, когда придет.


          1. artemmalko
            09.03.2016 13:56

            В будущем будет стек, как раз. А настоящее — это symbols.


            1. extempl
              09.03.2016 15:51

              Всё-таки symbols не дают свободу настройки изображений с несколькими цветами.


  1. xtech
    09.03.2016 13:45

    Да вот еще добавлю, при сложных svg можно запутаться с назначением цветов, Как мне узнать в каком порядке цвета, если иконку делал не я?


    1. psrafo
      09.03.2016 13:52

      Это проблема, да, но лично для меня и моей команды она не является таковым, ибо даже если мы скачаем какую-то иконку, дизайнер все равно что-то с ним делает, прежде чем она доберется до разработчика.


  1. Nadoedalo
    09.03.2016 16:57
    -1

    эм, а смысл если можно:
    1) Рано или поздно вставлять картинки-svg в shadow dom(в таком случае это будет ~аналогично 1 методу без недостатка "раздутие оригинального DOM"). Там, вообщем-то, даже аргументы будут
    2) Взять ровно тот-же скрипт который будет искать определённые html-теги и напрямую их вставлять в нужные места, а всё остальное — с помощью CSS?

    вообщем преимущества спорны. Очередной костыль в котором никто кроме автора не разберётся + среда разработки будет ругаться и выдавать, как минимум — варнинги.

    + автор, ты представляешь сколько из-за смены цвета картинки будет перерисована вся страница? Любое изменение css заставляет страницу отрендерится заново всю, потому что неизвестно какие элементы могли быть задеты.


    1. psrafo
      09.03.2016 18:25

      Если вы в этом не видите смысла, наверное, оно не для вас.

      Очередной костыль в котором никто кроме автора не разберётся

      Я не настаиваю, возможно моя реализация отстойна, она просто идет в бонус. Я просто предлагаю способ/метод/подход к решению проблемы.

      сколько из-за смены цвета картинки будет перерисована вся страница?

      Нет, я не представляю, сколько раз вся страница будет перерисована, потому что она этого делать не будет. Данный метод для браузера ничем не отличается от тех, что мы используем сейчас.

      Да и вообще, вы вцепились в мою реализацию, но ведь статья не про него — главное метод.

      1) Рано или поздно вставлять картинки-svg в shadow dom(в таком случае это будет ~аналогично 1 методу без недостатка "раздутие оригинального DOM"). Там, вообщем-то, даже аргументы будут
      2) Взять ровно тот-же скрипт который будет искать определённые html-теги и напрямую их вставлять в нужные места, а всё остальное — с помощью CSS?

      Не могу найти связь с нашей темой, извините.


  1. AlexLeonov
    09.03.2016 17:46
    +1

    Гм.

    Шаг первый. Настраиваем сервер так, чтобы он существующие файлы отдавал напрямик, а при запросе несуществующих — отправлял запрос на нечто вроде image.php

    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-l
    RewriteRule ^(.*)$ image.php [QSA]

    или

    location / {
        try_files $uri $uri/ /image.php?$args;
    }

    Шаг 2.
    В image.php понимаем, что нам надо, используем всю мощь разбора URL, формируем из шаблона SVG готовый SVG подстановкой параметров, кладем его в заранее определенную папочку на сервере и отдаем клиенту.

    Шаг 3.
    Кто-то приходит за картинкой второй раз. См. шаг 1.


    1. psrafo
      09.03.2016 18:29

      Ага, добавлю ссылку в конце статьи, спасибо.


      1. AlexLeonov
        09.03.2016 21:52

        Да не за что. Это совершенно стандартный и избитый способ формирования, например, миниатюр

        src="bigpicture.w200.jpg"
        =>
        image.php?request=bigpicture.w200.jpg

        ну а дальше уже любой PHP-шник вам накидает более-менее нормальный скриптик за 15 минут, если дадите ему четкие инструкции, что и куда нужно подставлять