Зачем я написала плагин для Grunt

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

Примерные шаги:

  1. Покрасить каждую иконку в нужный цвет.
  2. Для некоторых иконок сделать вариации другого цвета и размера.
  3. Собрать в PNG-спрайт.
  4. Написать CSS.

При этом получается две версии картинок: прозрачные исходники и те, что в спрайте. Обе версии всегда должны синхронизироваться между собой, а в идеале — иметь общий CSS c общими размерами и заливками для SVG.

Очевидно, что делать сборку руками долго, и процесс нужно автоматизировать, особенно если иметь в виду необходимость синхронизации изменений.

Если опустить второй шаг, первоначальную версию можно собрать из существующих плагинов для гранта. Правда, получается нечто странное, но конструкция работает.

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

  1. grunt-grunticon — красит и создает PNG-версии.
  2. grunt-spritesmith — генерит спрайт и CSS.

Это позволило быстро получить некий промежуточный результат, но меня не устроил CSS, получаемый на выходе. Например, он не содержал цвета заливки для SVG, и их потребовалось бы задавать где-то отдельно. Кроме того, чтобы получить в спрайте вариации иконок другого цвета и размера, пришлось бы дублировать SVG-исходники, а чтобы grunticon покрасил SVG-файлы в нужные цвета, их все нужно переназвать специальным образом (а перед генерацией спрайта переназвать обратно, ага).

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

Мой плагин: svg_fallback.

(Да, я знаю что лучше использовать дефис, а ещё в названии не хватает префикса grunt, но это мой первый плагин : )

Что он делает:

  1. Берет папку SVG-изображений и делает из неё библиотеку символов.
  2. Берет ещё раз ту же папку, собирает из картинок спрайт и генерит CSS с заливками, размерами и фоновой картинкой для старых браузеров.

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

По окончании работы открывается демо-страница с результатами работы плагина (можно проверить работу и SVG, и PNG).

Выше описан самый простой сценарий, при котором исходные файлы никак не модифицируются в процессе, но самое интересное начинается, если в папку с исходниками положить файл config.json, в котором описаны желаемые изменения.

Например, можно задать цвет, который получат все иконки по умолчанию:

{
  "color": "orangered"
}

Сейчас плагин умеет красить только прозрачные иконки.

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

{
  "default-sizes": {
    "heart": {
      "width": 182
      },
    "home": {
      "height": 42
    }
  }
}

Сами SVG на странице могут быть любого размера, но для генерации PNG+CSS нужно привести исходники к конкретным размерам, которыми потом планируется пользоваться.

А вот так можно сделать несколько вариаций одного файла:

{
  "icons": {
    "heart": [{
      "width": 50
    }, {
      "color": "green"
    }, {
      "width": 150,
      "color": "steelblue"
    }],
    "home": [{
      "width": 150
    }, {
      "width": 170,
      "color": "teal"
    }, {
      "height": 62,
      "color": "yellowgreen"
    }]
  }
}

В этом случае на выходе будет, например, исходный файл heart.svg и его вариации:

heart--w50.svg
heart--green.svg
heart--w150--steelblue.svg

Можно задавать конфигурацию по частям, а можно написать всё сразу:

{
  "color": "orangered",
  "default-sizes": {
    // размеры по умолчанию
  },
  "icons": {
    // вариации иконок
  }
}

Результатом работы плагина будут три файла, названные по имени родительской папки. Например, содержимое папки myicons/ превратится в:

  • myicons.svg — библиотека символов
  • myicons.png — спрайт
  • myicons.css — CSS с размерами иконок и заливкой для SVG.

Для использования получившихся иконок на странице нужно:

  1. Подключить на страницу myicons.css.
  2. В начале документа (после открывающего <body>) разместить библиотеку иконок (содержимое файла myicons.svg).
  3. Отдельные иконки на страницу вставляются вот таким образом:
<svg xmlns="http://www.w3.org/2000/svg" class="myicons myicons--heart">
 <use xlink:href="#myicons--heart"></use>
</svg>

Upd: если в SVG опустить закрывающие теги (написать <use/>, a не <use>...</use>), в старых браузерах перестаёт работать HTML5-разметка. Всем SVG-тегам на странице обязательно нужен закрывающий парный тег. За уточнение спасибо mista_k.

Отображение иконки в старых браузерах обеспечивается вот таким кодом:

.ie8 .myicons {
  background-image: url(noconfig.png);
}

Подразумевается, что на странице есть класс .ie8, по которому можно определить нужную версию IE.

Чтобы лучше понять как всё работает, можете установить пакет и заглянуть в папку svg_fallback/test/.

В процессе написания этого плагина было принято решение вынести код, отвечающий за изменение файлов, в отдельный пакет: svg-modify, который, при желании, можно использовать в виде плагина для гранта: grunt-svg-modify.

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

Если вы захотите их использовать, следует иметь в виду, что svg-modify, скорее всего, ещё будет дорабатываться (хочется более разумной работы с цветами), да и вообще я всё ещё изредка нахожу в плагинах баги разного размера, так что их стабильную работу обещать пока не могу (хотя и постараюсь).

Кроме того, svg_fallback писался для конкретной задачи, и, возможно, для ваших целей вам захочется изменить именование файлов или формат вывода CSS. В плагине нет таких опций, но вы можете форкнуть его и переписать на свой вкус.

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

Upd: за помощь и ценные советы спасибо mista_k и alexeyten.

Ссылки по теме:
svg_fallback
npmjs.org/~yoksel
gruntjs.com
Грабли на чистом SVG
You Don't Know SVGГрабли на чистом SVG
Наверх