paint-brush
Como criar Sprite SVG com íconespor@gmakarov
3,193 leituras
3,193 leituras

Como criar Sprite SVG com ícones

por German Makarov10m2023/12/23
Read on Terminal Reader

Muito longo; Para ler

Os desenvolvedores costumam inserir SVG diretamente no JSX. Isso é conveniente de usar, mas aumenta o tamanho do pacote JS. Na busca pela otimização, decidi encontrar outra maneira de usar ícones SVG sem sobrecarregar o pacote. Falaremos sobre sprites SVG, o que são, como usá-los e quais ferramentas estão disponíveis para trabalhar com eles. Começando pela teoria, escreveremos um script que gera um sprite SVG passo a passo e concluiremos discutindo plugins para vite e webpack.
featured image - Como criar Sprite SVG com ícones
German Makarov HackerNoon profile picture

Os desenvolvedores costumam inserir SVG diretamente no JSX. Isso é conveniente de usar, mas aumenta o tamanho do pacote JS. Na busca pela otimização, decidi encontrar outra maneira de usar ícones SVG sem sobrecarregar o pacote. Falaremos sobre sprites SVG, o que são, como usá-los e quais ferramentas estão disponíveis para trabalhar com eles.


Começando pela teoria, escreveremos um script que gera um sprite SVG passo a passo e concluiremos discutindo plugins para vite e webpack .

O que é Sprite SVG?

Um sprite de imagem é uma coleção de imagens colocadas em uma única imagem. Por sua vez, SVG Sprite é uma coleção de conteúdo SVG, agrupado em <symbol /> , que é colocado em <svg /> .


Por exemplo, temos um ícone de caneta SVG simples:


Para obter um sprite SVG, substituiremos a tag <svg /> por <symbol /> e envolveremos <svg /> externamente:

Agora é um sprite SVG e temos um ícone dentro com id="icon-pen" .


Ok, mas devemos descobrir como colocar esse ícone em nossa página HTML. Usaremos a tag <use /> com o atributo href , especificando o ID do ícone, e duplicará este elemento dentro do SVG.


Vamos dar uma olhada em um exemplo de como funciona <use /> :

Neste exemplo, existem dois círculos. O primeiro tem contorno azul e o segundo é uma duplicata do primeiro, mas com preenchimento vermelho.


Voltemos ao nosso sprite SVG. Junto com o uso de <use /> , obteremos isto:

Aqui temos um botão com nosso ícone de caneta.


Até agora, usamos um ícone sem seu código em <button /> . Se tivermos mais de um botão na página, isso não afetará mais de uma vez o tamanho do nosso layout HTML, pois todos os ícones virão do nosso sprite SVG e serão reutilizáveis.

Criando arquivo SVG Sprite

Vamos mover nosso sprite SVG para um arquivo separado para que não tenhamos que sobrecarregar o arquivo index.html . Primeiro, crie um arquivo sprite.svg e coloque um sprite SVG nele. A próxima etapa é fornecer acesso ao ícone usando o atributo href em <use/> :

Automatizando a criação de Sprites SVG

Para economizar muito tempo no uso de ícones, vamos configurar uma automação para esse processo. Para ter fácil acesso aos ícones e gerenciá-los como quisermos, eles devem ser separados, cada um em seu arquivo.


Primeiramente devemos colocar todos os ícones na mesma pasta, por exemplo:

Agora, vamos escrever um script que capture esses arquivos e os combine em um único sprite SVG.


  1. Crie o arquivo generateSvgSprite.ts no diretório raiz do seu projeto.


  2. Instale a biblioteca glob :

     npm i -D glob


  3. Obtenha uma série de caminhos completos para cada ícone usando globSync :

  4. Agora, iremos iterar cada caminho de arquivo e obter o conteúdo do arquivo usando a biblioteca interna do Node fs :

    Ótimo, temos o código SVG de cada ícone e agora podemos combiná-los, mas devemos substituir a tag svg dentro de cada ícone pela tag de symbol e remover atributos SVG inúteis.


  5. Deveríamos analisar nosso código SVG com alguma biblioteca de analisador HTML para obter sua representação DOM. Usarei node-html-parser :

    Analisamos o código SVG e obtivemos o elemento SVG como se fosse um elemento HTML real.


  6. Usando o mesmo analisador, crie um elemento symbol vazio para mover os filhos de svgElement para symbol :

  7. Depois de extrair os filhos de svgElement , também devemos obter os atributos id e viewBox dele. Como id , vamos definir o nome do arquivo de ícone.

  8. Agora temos um elemento symbol que pode ser colocado em um sprite SVG. Portanto, basta definir a variável symbols antes de iterar os arquivos, transformar o symbolElement em uma string e colocá-lo em symbols :

  9. A etapa final é criar o próprio sprite SVG. Representa uma string com svg em “root” e símbolos como filhos:

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


    E se você não está pensando em usar plugins, dos quais falarei a seguir, você precisa colocar o arquivo com o sprite criado em alguma pasta estática. A maioria dos bundlers usa uma pasta public :

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


E é isso; o script está pronto para uso:

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


Você pode colocar este script na raiz do seu projeto e executá-lo com tsx :

 npx tsx generateSvgSprite.ts


Na verdade, estou usando tsx aqui porque costumava escrever código em TypeScript em qualquer lugar, e esta biblioteca permite executar scripts de nó escritos em TypeScript. Se quiser usar JavaScript puro, você pode executá-lo com:

 node generateSvgSprite.js


Então, vamos resumir o que o script está fazendo:

  • Ele procura na pasta src/icons qualquer arquivo .svg .


  • Extrai o conteúdo de cada ícone e cria um elemento de símbolo a partir dele.


  • Ele reúne todos os símbolos em um único <svg />.


  • Ele cria o arquivo sprite.svg na pasta public .

Como alterar as cores dos ícones

Vamos abordar um caso frequente e importante: as cores! Criamos um script onde o ícone entra em um sprite, mas esse ícone pode ter cores diferentes ao longo do projeto.


Devemos ter em mente que não apenas os elementos <svg/> podem ter atributos de preenchimento ou traço, mas também path , circle , line e outros. Há um recurso CSS muito útil que nos ajudará - currentcolor .


Esta palavra-chave representa o valor da propriedade color de um elemento. Por exemplo, se usarmos color: red em um elemento que possui um background: currentcolor , então este elemento terá um fundo vermelho.


Basicamente, precisamos alterar cada valor de atributo de traço ou preenchimento para currentcolor . Espero que você não esteja vendo isso feito manualmente, heh. E mesmo escrever algum código que substitua ou analise strings SVG não é muito eficiente em comparação com uma ferramenta muito útil svgo .


Este é um otimizador SVG que pode ajudar não apenas com cores, mas também na remoção de informações redundantes do SVG.


Vamos instalar o svgo :

 npm i -D svgo


svgo possui plug-ins integrados, e um deles é convertColors , que possui a propriedade currentColor: true . Se usarmos esta saída SVG, ela substituirá cores por currentcolor . Aqui está o uso de svgo junto com 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);


E a saída será:

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


Vamos adicionar svgo ao nosso script mágico que escrevemos na parte anterior:

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


E execute o script:

 npx tsx generateSvgSprite.ts


Como resultado, o sprite SVG conterá ícones com currentColor . E esses ícones podem ser usados em qualquer lugar do projeto com qualquer cor que você desejar.

Plug-ins

Temos um script e podemos executá-lo quando quisermos, mas é um pouco inconveniente usá-lo manualmente. Portanto, recomendo alguns plug-ins que podem visualizar nossos arquivos .svg e gerar sprites SVG em qualquer lugar:


  1. vite-plugin-svg-spritemap (para usuários vite )

    Este é o meu plugin que contém basicamente este script que acabamos de criar neste artigo. O plugin tem a substituição currentColor habilitada por padrão, então você pode configurar o plugin com bastante facilidade.

     // 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 (para usuários do webpack )

    Usei este plugin Webpack até mudar para o Vite. Mas este plugin ainda é uma boa solução se você estiver usando o Webpack. Você deve ativar manualmente a conversão de cores e ficará assim:

     // 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', }, }), ], }

Uso em Layout

Darei um exemplo em React , mas você pode implementá-lo onde quiser porque se trata principalmente de HTML. Então, como temos sprite.svg em nossa pasta build, podemos acessar o arquivo sprite e criar o componente Icon básico:

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

O resultado final

Então, resumindo tudo, para evitar muito trabalho manual com ícones, nós:

  • pode facilmente salvar e manter ícones organizados no projeto com nomes desejados


  • temos um script que combina todos os ícones em um único sprite em um arquivo separado que reduz o tamanho do pacote e nos permite usar esses ícones em qualquer lugar do projeto


  • temos uma ferramenta útil que nos ajuda a manter os ícones livres de atributos desnecessários e alterar as cores no local de uso


  • temos um plugin que pode observar nossos arquivos de ícones e gerar sprites em movimento como parte do processo de construção


  • tem um componente Icon que é uma cereja no topo

Conclusão

A eficiência no desenvolvimento não envolve apenas economia de tempo; trata-se de desbloquear nosso potencial criativo. Automatizar tarefas essenciais, como gerenciar ícones, não é apenas um atalho; é uma porta de entrada para uma experiência de codificação mais suave e impactante. E economizando tempo nessas tarefas rotineiras, você pode se concentrar em tarefas mais complexas e crescer como desenvolvedor com mais rapidez.