Как создать 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.
- Создайте файл
generateSvgSprite.ts
в корневом каталоге вашего проекта.
ол> - Он просматривает папку
src/icons
на наличие любых файлов.svg
. - vite-plugin-svg-spritemap (для пригласить пользователей) ол>
- можно легко сохранять и систематизировать значки в проекте с желаемыми именами.
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
Итак, подведем итоги того, что делает скрипт:
* Он извлекает содержимое каждого значка и создает из него элемент символа.
* Он объединяет все символы в один <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 на ходу:
Это мой плагин, который по сути содержит скрипт, который мы только что создали в этой статье. По умолчанию в плагине включена замена 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, который является вишенкой на торте
Заключение
Эффективность разработки — это не только экономия времени; речь идет о раскрытии нашего творческого потенциала. Автоматизация таких мельчайших задач, как управление значками, — это не просто ярлык; это путь к более плавному и эффективному программированию. А сэкономив время на таких рутинных вещах, вы сможете сосредоточиться на более сложных задачах и быстрее расти как разработчик.
Оригинал