paint-brush
Comment créer un sprite SVG avec des icônespar@gmakarov
5,565 lectures
5,565 lectures

Comment créer un sprite SVG avec des icônes

par German Makarov10m2023/12/23
Read on Terminal Reader

Trop long; Pour lire

Les développeurs insèrent souvent SVG directement dans JSX. C'est pratique à utiliser, mais cela augmente la taille du bundle JS. Dans un souci d'optimisation, j'ai décidé de trouver une autre façon d'utiliser les icônes SVG sans encombrer le bundle. Nous parlerons des sprites SVG, de ce qu'ils sont, comment les utiliser et quels outils sont disponibles pour travailler avec eux. En commençant par la théorie, nous allons écrire un script qui génère un sprite SVG étape par étape et conclure en discutant des plugins pour vite et webpack.
featured image - Comment créer un sprite SVG avec des icônes
German Makarov HackerNoon profile picture

Les développeurs insèrent souvent SVG directement dans JSX. C'est pratique à utiliser, mais cela augmente la taille du bundle JS. Dans un souci d'optimisation, j'ai décidé de trouver une autre façon d'utiliser les icônes SVG sans encombrer le bundle. Nous parlerons des sprites SVG, de ce qu'ils sont, comment les utiliser et quels outils sont disponibles pour travailler avec eux.


En commençant par la théorie, nous allons écrire un script qui génère un sprite SVG étape par étape et conclure en discutant des plugins pour vite et webpack .

Qu’est-ce que le Sprite SVG ?

Un sprite d’image est une collection d’images placées dans une seule image. À son tour, SVG Sprite est une collection de contenu SVG, enveloppé dans <symbol /> , qui est placé dans <svg /> .


Par exemple, nous avons une simple icône de stylo SVG :


Pour obtenir un sprite SVG, nous allons remplacer la balise <svg /> par <symbol /> , et l'envelopper avec <svg /> en externe :

C'est maintenant un sprite SVG, et nous avons une icône à l'intérieur avec id="icon-pen" .


Ok, mais nous devrions trouver comment placer cette icône sur notre page HTML. Nous utiliserons la balise <use /> avec l'attribut href , en spécifiant l'ID de l'icône, et elle dupliquera cet élément dans SVG.


Jetons un coup d'œil à un exemple du fonctionnement de <use /> :

Dans cet exemple, il y a deux cercles. Le premier a un contour bleu et le second est une copie du premier mais avec un remplissage rouge.


Revenons à notre sprite SVG. En plus de l'utilisation de <use /> , nous obtiendrons ceci :

Ici, nous avons un bouton avec notre icône de stylo.


Jusqu'à présent, nous avons utilisé une icône sans son code dans <button /> . Si nous avons plus d'un bouton sur la page, cela n'affectera pas plus d'une fois la taille de notre mise en page HTML car toutes les icônes proviendront de notre sprite SVG et seront réutilisables.

Création d'un fichier Sprite SVG

Déplaçons notre sprite SVG dans un fichier séparé afin de ne pas avoir à encombrer le fichier index.html . Tout d’abord, créez un fichier sprite.svg et insérez-y un sprite SVG. L'étape suivante consiste à fournir l'accès à l'icône en utilisant l'attribut href dans <use/> :

Automatisation de la création de sprites SVG

Pour gagner beaucoup de temps sur l'utilisation des icônes, mettons en place une automatisation pour ce processus. Pour accéder facilement aux icônes et les gérer comme nous le souhaitons, il faut les séparer, chacune dans son propre fichier.


Tout d’abord, nous devrions mettre toutes les icônes dans le même dossier, par exemple :

Maintenant, écrivons un script qui récupère ces fichiers et les combine en un seul sprite SVG.


  1. Créez le fichier generateSvgSprite.ts dans le répertoire racine de votre projet.


  2. Installez la bibliothèque globale :

     npm i -D glob


  3. Obtenez un tableau de chemins complets pour chaque icône en utilisant globSync :

  4. Maintenant, nous allons parcourir chaque chemin de fichier et obtenir le contenu du fichier à l'aide de la bibliothèque intégrée de Node fs :

    Super, nous avons le code SVG de chaque icône, et maintenant nous pouvons les combiner, mais nous devrions remplacer la balise svg à l'intérieur de chaque icône par la balise symbol et supprimer les attributs SVG inutiles.


  5. Nous devrions analyser notre code SVG avec une bibliothèque d'analyseurs HTML pour obtenir sa représentation DOM. J'utiliserai node-html-parser :

    Nous avons analysé le code SVG et obtenu l'élément SVG comme s'il s'agissait d'un véritable élément HTML.


  6. En utilisant le même analyseur, créez un élément symbol vide pour déplacer les enfants de svgElement vers symbol :

  7. Après avoir extrait les enfants de svgElement , nous devrions également en obtenir les attributs id et viewBox . En tant id , définissons le nom du fichier icône.

  8. Nous avons maintenant un élément symbol qui peut être placé dans un sprite SVG. Il suffit donc de définir la variable symbols avant d'itérer les fichiers, de transformer le symbolElement en chaîne et de le pousser en symbols :

  9. La dernière étape consiste à créer le sprite SVG lui-même. Il représente une chaîne avec svg en « racine » et des symboles comme enfants :

     const svgSprite = `<svg>${symbols.join('')}</svg>`;


    Et si vous n'envisagez pas d'utiliser les plugins, dont je parlerai ci-dessous, vous devez placer le fichier avec le sprite créé dans un dossier statique. La plupart des bundlers utilisent un dossier public :

     fs.writeFileSync('public/sprite.svg', svgSprite);


Et c'est tout ; le script est prêt à être utilisé :

 // 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);


Vous pouvez mettre ce script à la racine de votre projet et l'exécuter avec tsx :

 npx tsx generateSvgSprite.ts


En fait, j'utilise tsx ici parce que j'écrivais du code en TypeScript partout, et cette bibliothèque vous permet d'exécuter des scripts de nœuds écrits en TypeScript. Si vous souhaitez utiliser du JavaScript pur, vous pouvez l'exécuter avec :

 node generateSvgSprite.js


Alors, résumons ce que fait le script :

  • Il recherche dans le dossier src/icons tous les fichiers .svg .


  • Il extrait le contenu de chaque icône et en crée un élément de symbole.


  • Il regroupe tous les symboles dans un seul <svg />.


  • Il crée le fichier sprite.svg dans le dossier public .

Comment changer les couleurs des icônes

Abordons un cas fréquent et important : les couleurs ! Nous avons créé un script dans lequel l'icône va dans un sprite, mais cette icône peut avoir des couleurs différentes tout au long du projet.


Nous devons garder à l’esprit que non seulement les éléments <svg/> peuvent avoir des attributs fill ou Stroke , mais aussi path , circle , line et autres. Il existe une fonctionnalité CSS très utile qui nous aidera : currentcolor .


Ce mot-clé représente la valeur de la propriété color d'un élément. Par exemple, si on utilise la color: red sur un élément qui a un background: currentcolor , alors cet élément aura un fond rouge.


Fondamentalement, nous devons remplacer chaque valeur d'attribut de trait ou de remplissage par la valeur currentcolor . J'espère que vous ne le voyez pas fait manuellement, hein. Et même écrire du code qui remplacera ou analysera les chaînes SVG n'est pas très efficace par rapport à un outil très utile svgo .


Il s'agit d'un optimiseur SVG qui peut aider non seulement avec les couleurs, mais également à supprimer les informations redondantes du SVG.


Installons svgo :

 npm i -D svgo


svgo a des plugins intégrés, et l'un d'eux est convertColors , qui a la propriété currentColor: true . Si nous utilisons cette sortie SVG, elle remplacera les couleurs par currentcolor . Voici l'utilisation de svgo avec 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);


Et le résultat sera :

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


Ajoutons svgo dans notre script magique que nous avons écrit dans la partie précédente :

 // 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);


Et exécutez le script :

 npx tsx generateSvgSprite.ts


Par conséquent, le sprite SVG contiendra des icônes avec currentColor . Et ces icônes peuvent être utilisées partout dans le projet avec la couleur de votre choix.

Plugins

Nous avons un script et nous pouvons l'exécuter quand nous le voulons, mais il est légèrement gênant de l'utiliser manuellement. Je recommande donc quelques plugins capables de visualiser nos fichiers .svg et de générer des sprites SVG en déplacement :


  1. vite-plugin-svg-spritemap (pour les utilisateurs vite )

    Ceci est mon plugin qui contient essentiellement ce script que nous venons de créer dans cet article. Le plugin a le remplacement currentColor activé par défaut, vous pouvez donc configurer le plugin assez facilement.

     // vite.config.ts import svgSpritemap from 'vite-plugin-svg-spritemap'; export default defineConfig({ plugins: [ svgSpritemap({ pattern: 'src/icons/*.svg', filename: 'sprite.svg', }), ], });


  2. svg-spritemap-webpack-plugin (pour les utilisateurs du webpack )

    J'ai utilisé ce plugin Webpack jusqu'à ce que je passe à Vite. Mais ce plugin reste une bonne solution si vous utilisez Webpack. Vous devez activer manuellement la conversion des couleurs, et cela ressemblera à ceci :

     // webpack.config.js const SVGSpritemapPlugin = require('svg-spritemap-webpack-plugin'); module.exports = { plugins: [ new SVGSpritemapPlugin('src/icons/*.svg', { output: { svgo: { plugins: [ { name: 'convertColors', params: { currentColor: true, }, }, ], }, filename: 'sprite.svg', }, }), ], }

Utilisation dans la mise en page

Je vais fournir un exemple dans React , mais vous pouvez l'implémenter où vous le souhaitez car il s'agit principalement de HTML. Ainsi, comme nous avons sprite.svg dans notre dossier build, nous pouvons accéder au fichier sprite et créer le composant Icon de base :

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

Le résultat final

Donc, en résumant le tout, pour éviter beaucoup de travail manuel avec les icônes, nous :

  • peut facilement enregistrer et conserver des icônes organisées dans le projet avec des noms souhaitables


  • avoir un script qui combine toutes les icônes en un seul sprite dans un fichier séparé qui réduit la taille du bundle et nous permet d'utiliser ces icônes n'importe où dans le projet


  • avoir un outil utile qui nous aide à garder les icônes désencombrées des attributs inutiles et à changer les couleurs sur le lieu d'utilisation


  • avoir un plugin qui peut regarder nos fichiers d'icônes et générer des sprites en déplacement dans le cadre du processus de construction


  • avoir un composant Icon qui est une cerise sur le gâteau

Conclusion

L'efficacité du développement ne consiste pas seulement à gagner du temps ; il s'agit de libérer notre potentiel créatif. L'automatisation des tâches essentielles comme la gestion des icônes n'est pas seulement un raccourci ; c'est une passerelle vers une expérience de codage plus fluide et plus percutante. Et en gagnant du temps sur des tâches aussi routinières, vous pouvez vous concentrer sur des tâches plus complexes et évoluer plus rapidement en tant que développeur.