Как создать SVG-спрайт с иконками

Как создать SVG-спрайт с иконками

24 декабря 2023 г.

Разработчики часто вставляют SVG непосредственно в JSX. Это удобно в использовании, но увеличивает размер пакета JS. В поисках оптимизации я решил найти другой способ использования SVG-значков, не загромождая пакет. Мы поговорим о SVG-спрайтах, что это такое, как их использовать и какие инструменты доступны для работы с ними.

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

Что такое SVG-спрайт?

Спрайт изображения — это набор изображений, помещенных в одно изображение. В свою очередь, SVG Sprite представляет собой коллекцию SVG-контента, завернутую в <symbol />, который помещается в <svg />.

Например, у нас есть простой значок пера в формате SVG:

Чтобы получить спрайт SVG, мы заменим тег <svg /> на <symbol /> и обернем его <svg / > внешне:

Теперь это SVG-спрайт, и внутри у нас есть значок с id="icon-pen".

Хорошо, но нам нужно придумать, как разместить этот значок на нашей HTML-странице. Мы будем использовать тег <use /> с атрибутом href, указывающим идентификатор значка, и он будет дублировать этот элемент внутри SVG.

Давайте рассмотрим пример того, как работает <use />:

В этом примере есть два круга. Первый имеет синий контур, а второй — дубликат первого, но с красной заливкой.

Давайте вернемся к нашему SVG-спрайту. Вместе с использованием <use /> мы получим следующее:

Здесь у нас есть кнопка со значком пера.

До сих пор мы использовали значок без его кода в <button />. Если на странице имеется более одной кнопки, это не повлияет на размер нашего HTML-макета более одного раза, поскольку все значки будут взяты из нашего спрайта SVG и их можно будет использовать повторно.

Создание файла спрайта SVG

Давайте переместим наш SVG-спрайт в отдельный файл, чтобы не загромождать файл index.html. Сначала создайте файл sprite.svg и поместите в него спрайт SVG. Следующий шаг — предоставить доступ к значку с помощью атрибута href в <use/>:

.

Автоматизация создания SVG-спрайтов

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

Во-первых, нам нужно поместить все значки в одну папку, например:

Теперь давайте напишем сценарий, который захватывает эти файлы и объединяет их в один спрайт SVG.

  1. Создайте файл generateSvgSprite.ts в корневом каталоге вашего проекта.
  2. 2. Установите библиотеку glob:

    bash npm я -D glob

    3. Получите массив полных путей для каждого значка, используя globSync:

    ![](https://cdn.hackernoon.com/images/uKHFuSMAOXQpD3Hj26VzTmLymjn1-g593wkh.jpeg)
    
      <ли>

      Теперь мы пройдемся по каждому пути к файлу и получим содержимое файла, используя встроенную библиотеку Node fs :

      Отлично, у нас есть SVG-код каждой иконки, и теперь мы можем их объединить, но нам нужно заменить тег svg внутри каждой иконки на тег symbol и удалите ненужные атрибуты SVG.

      5. Нам следует проанализировать наш код SVG с помощью какой-либо библиотеки анализатора HTML, чтобы получить его представление DOM. Я буду использовать node-html-parser:

      ![](https://cdn.hackernoon.com/images/uKHFuSMAOXQpD3Hj26VzTmLymjn1-ffb3wce.png)
      

      Мы проанализировали код SVG и получили элемент SVG, как если бы это был настоящий элемент HTML.

      6. Используя тот же парсер, создайте пустой элемент symbol, чтобы переместить дочерние элементы svgElement в symbol:

      ![](https://cdn.hackernoon.com/images/uKHFuSMAOXQpD3Hj26VzTmLymjn1-bmc3wzh.png)
      
        <ли>

        После извлечения дочерних элементов из svgElement мы также должны получить из него атрибуты id и viewBox. В качестве id зададим имя файла значка.

        8. Теперь у нас есть элемент symbol, который можно поместить в спрайт SVG. Итак, просто определите переменную symbols перед итерацией файлов, преобразуйте symbolElement в строку и вставьте ее в symbols:

        9. Последний шаг — создание самого SVG-спрайта. Он представляет собой строку с svg в «корне» и символами в качестве дочерних элементов:

        машинописный текст const svgSprite = `<svg>${symbols.join('')}</svg>`;

        А если вы не планируете использовать плагины, о которых я расскажу ниже, вам нужно поместить файл с созданным спрайтом в какую-нибудь статическую папку. Большинство сборщиков используют папку public:

        машинописный текст fs.writeFileSync('public/sprite.svg', svgSprite);

        И вот оно; скрипт готов к использованию:

        // generateSvgSprite.ts
        
        import { globSync } from 'glob';
        import fs from 'fs';
        import { HTMLElement, parse } from 'node-html-parser';
        import path from 'path';
        
        const svgFiles = globSync('src/icons/*.svg');
        const symbols: string[] = [];
        
        svgFiles.forEach(file => {
          const code = fs.readFileSync(file, 'utf-8');
          const svgElement = parse(code).querySelector('svg') as HTMLElement;
          const symbolElement = parse('<symbol/>').querySelector('symbol') as HTMLElement;
          const fileName = path.basename(file, '.svg');
        
          svgElement.childNodes.forEach(child => symbolElement.appendChild(child));
        
          symbolElement.setAttribute('id', fileName);
        
          if (svgElement.attributes.viewBox) {
            symbolElement.setAttribute('viewBox', svgElement.attributes.viewBox);
          }
        
          symbols.push(symbolElement.toString());
        });
        
        const svgSprite = `<svg>${symbols.join('')}</svg>`;
        
        fs.writeFileSync('public/sprite.svg', svgSprite);
        

        Вы можете поместить этот скрипт в корень вашего проекта и запустить его с помощью tsx:

        npx tsx generateSvgSprite.ts
        

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

        node generateSvgSprite.js
        

        Итак, подведем итоги того, что делает скрипт:

        • Он просматривает папку src/icons на наличие любых файлов .svg.

        * Он извлекает содержимое каждого значка и создает из него элемент символа.

        * Он объединяет все символы в один <svg />.

        * Создается файл sprite.svg в папке public.

        Как изменить цвета значков

        Давайте рассмотрим один частый и важный случай: цвета! Мы создали скрипт, в котором значок превращается в спрайт, но на протяжении всего проекта этот значок может иметь разные цвета.

        Следует иметь в виду, что не только элементы <svg/> могут иметь атрибуты заливки или обводки, но также path, circle, < code>line и другие. Нам поможет очень полезная функция CSS — currentcolor.

        Это ключевое слово представляет значение свойства цвета элемента. Например, если мы используем color: red для элемента, имеющего background: currentcolor, тогда этот элемент будет иметь красный фон.

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

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

        Давайте установим svgo:

        npm i -D svgo
        

        svgo имеет встроенные плагины, и один из них — convertColors, имеющий свойство currentColor: true. Если мы используем этот вывод SVG, он заменит цвета на currentcolor. Вот использование svgo вместе с convertColors:

        import { optimize } from 'svgo';
        
        const output = optimize(
            '<svg viewBox="0 0 24 24"><path fill="#000" d="m15 5 4 4" /></svg>',
            {
              plugins: [
                    {
                  name: 'convertColors',
                  params: {
                    currentColor: true,
                  },
                }
                ],
            }
        )
        
        console.log(output);
        

        И результат будет:

        <svg viewBox="0 0 24 24"><path fill="currentColor" d="m15 5 4 4"/></svg>
        

        Давайте добавим svgo в наш волшебный скрипт, который мы написали в предыдущей части:

        // generateSvgSprite.ts
        
        import { globSync } from 'glob';
        import fs from 'fs';
        import { HTMLElement, parse } from 'node-html-parser';
        import path from 'path';
        import { Config as SVGOConfig, optimize } from 'svgo'; // import `optimize` function
        
        const svgoConfig: SVGOConfig = {
          plugins: [
            {
              name: 'convertColors',
              params: {
                currentColor: true,
              },
            }
          ],
        };
        
        const svgFiles = globSync('src/icons/*.svg');
        const symbols: string[] = [];
        
        svgFiles.forEach(file => {
          const code = fs.readFileSync(file, 'utf-8');
          const result = optimize(code, svgoConfig).data; // here goes `svgo` magic with optimization
          const svgElement = parse(result).querySelector('svg') as HTMLElement;
          const symbolElement = parse('<symbol/>').querySelector('symbol') as HTMLElement;
          const fileName = path.basename(file, '.svg');
        
          svgElement.childNodes.forEach(child => symbolElement.appendChild(child));
        
          symbolElement.setAttribute('id', fileName);
        
          if (svgElement.attributes.viewBox) {
            symbolElement.setAttribute('viewBox', svgElement.attributes.viewBox);
          }
        
          symbols.push(symbolElement.toString());
        });
        
        const svgSprite = `<svg xmlns="http://www.w3.org/2000/svg">${symbols.join('')}</svg>`;
        
        fs.writeFileSync('public/sprite.svg', svgSprite);
        

        И запустите скрипт:

        npx tsx generateSvgSprite.ts
        

        В результате спрайт SVG будет содержать значки с currentColor. И эти значки можно использовать везде в проекте, любого цвета.

        Плагины

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

        1. vite-plugin-svg-spritemap (для пригласить пользователей)
        2. Это мой плагин, который по сути содержит скрипт, который мы только что создали в этой статье. По умолчанию в плагине включена замена currentColor, поэтому вы можете легко настроить плагин.

          ```машинописный текст // vite.config.ts

          импортировать svgSpritemap из 'vite-plugin-svg-spritemap';

          экспортировать defineConfig по умолчанию({ плагины: [ svgSpritemap({ шаблон: 'src/icons/*.svg', имя файла: 'sprite.svg', }), ], }); ```

          2. svg-spritemap-webpack-plugin (для webpack пользователи)

          Я использовал этот плагин Webpack, пока не перешел на Vite. Но этот плагин по-прежнему является хорошим решением, если вы используете Webpack. Вам следует вручную включить преобразование цветов, и это будет выглядеть так:

          ```Javascript // webpack.config.js

          const SVGSpritemapPlugin = require('svg-spritemap-webpack-plugin');

          module.exports = { плагины: [ новый SVGSpritemapPlugin('src/icons/*.svg', { выход: { свго: { плагины: [ { имя: 'конвертировать цвета', параметры: { текущийЦвет: правда, }, }, ], }, имя файла: 'sprite.svg', }, }), ], } ```

          Использование в макете

          Я приведу пример в React, но вы можете реализовать его там, где захотите, поскольку в основном он касается HTML. Итак, поскольку у нас есть sprite.svg в нашей папке сборки, мы можем получить доступ к файлу спрайта и создать базовый компонент Icon:

          const Icon: FC<{ name: string }> = ({ name }) => (
            <svg>
              <use href={`/sprite.svg#${name}`} />
            </svg>
          );
          
          const App = () => {
            return <Icon name="pen" />;
          };
          

          Окончательный результат

          Итак, резюмируя всё, чтобы избежать большого количества ручной работы с иконками, мы:

          • можно легко сохранять и систематизировать значки в проекте с желаемыми именами.

          * иметь скрипт, который объединяет все значки в один спрайт в отдельном файле, что уменьшает размер пакета и позволяет нам использовать эти значки в любом месте проекта

          * есть полезный инструмент, который помогает нам очищать значки от ненужных атрибутов и менять цвета по месту использования

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

          * иметь компонент Icon, который является вишенкой на торте

          Заключение

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


          Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE