paint-brush
アイコンを含む SVG スプライトを作成する方法@gmakarov
3,193 測定値
3,193 測定値

アイコンを含む SVG スプライトを作成する方法

German Makarov10m2023/12/23
Read on Terminal Reader

長すぎる; 読むには

開発者は多くの場合、SVG を JSX に直接挿入します。これは便利ですが、JS バンドルのサイズが大きくなります。最適化を追求する中で、バンドルを乱雑にせずに SVG アイコンを使用する別の方法を見つけることにしました。 SVG スプライトとは何か、その使用方法、およびそれらを操作するために使用できるツールについて説明します。 理論から始めて、SVG スプライトを段階的に生成するスクリプトを作成し、最後に vite と webpack のプラグインについて説明します。
featured image - アイコンを含む SVG スプライトを作成する方法
German Makarov HackerNoon profile picture

開発者は多くの場合、SVG を JSX に直接挿入します。これは便利ですが、JS バンドルのサイズが大きくなります。最適化を追求する中で、バンドルを乱雑にせずに SVG アイコンを使用する別の方法を見つけることにしました。 SVG スプライトとは何か、その使用方法、およびそれらを操作するために使用できるツールについて説明します。


理論から始めて、SVG スプライトを段階的に生成するスクリプトを作成し、最後にvitewebpackのプラグインについて説明します。

SVGスプライトとは何ですか?

イメージ スプライトは、単一のイメージに配置されたイメージのコレクションです。 SVG スプライトは SVG コンテンツのコレクションであり、 <symbol />にラップされ、 <svg />に配置されます。


たとえば、単純な SVG ペン アイコンがあります。


SVG スプライトを取得するには、 <svg />タグを<symbol />に置き換え、外部で<svg />でラップします。

これは SVG スプライトになり、その中にid="icon-pen"アイコンが含まれています。


わかりましたが、このアイコンを HTML ページに配置する方法を考えなければなりません。 <use />タグをhref属性とともに使用し、アイコンの ID を指定します。これにより、SVG 内でこの要素が複製されます。


<use />どのように機能するかの例を見てみましょう。

この例では、円が 2 つあります。最初のものは青い輪郭を持ち、2 つ目は最初のものの複製ですが、赤い塗りつぶしが施されています。


SVG スプライトに戻りましょう。 <use />を使用すると、次のようになります。

ここには、ペンのアイコンが付いたボタンがあります。


これまでは、 <button />内のコードなしでアイコンを使用してきました。ページ上に複数のボタンがある場合、すべてのアイコンは SVG スプライトから取得され、再利用できるため、HTML レイアウトのサイズに複数回影響することはありません。

SVGスプライトファイルの作成

SVG スプライトを別のファイルに移動して、 index.htmlファイルを乱雑にしないようにしましょう。まず、 sprite.svgファイルを作成し、その中に SVG スプライトを配置します。次のステップでは、 <use/>href属性を使用してアイコンへのアクセスを提供します。

SVG スプライト作成の自動化

アイコンの使用時間を大幅に節約するために、このプロセスの自動化を設定しましょう。アイコンに簡単にアクセスして必要に応じて管理するには、アイコンをそれぞれ独自のファイルに分離する必要があります。


まず、すべてのアイコンを同じフォルダーに配置する必要があります。例:

次に、これらのファイルを取得して単一の SVG スプライトに結合するスクリプトを作成しましょう。


  1. プロジェクトのルート ディレクトリにgenerateSvgSprite.tsファイルを作成します。


  2. グロブライブラリをインストールします。

     npm i -D glob


  3. globSyncを使用して、各アイコンの絶対パスの配列を取得します。

  4. ここで、各ファイル パスを反復処理し、Node の組み込みライブラリfsを使用してファイルのコンテンツを取得します。

    各アイコンの SVG コードがあり、それらを結合できるようになりました。ただし、各アイコン内のsvgタグをsymbolタグに置き換え、不要な SVG 属性を削除する必要があります。


  5. SVG コードを HTML パーサー ライブラリで解析して、DOM 表現を取得する必要があります。 node-html-parser を使用します。

    SVG コードを解析し、実際の HTML 要素であるかのように SVG 要素を取得しました。


  6. 同じパーサーを使用して、空のsymbol要素を作成し、 svgElementの子をsymbolに移動します。

  7. svgElementから子を抽出した後、そこからidviewBox属性も取得する必要があります。 idにはアイコンファイルの名前を設定しましょう。

  8. これで、SVG スプライトに配置できるsymbol要素ができました。したがって、ファイルを反復処理する前に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 />.


  • publicフォルダーにsprite.svgファイルが作成されます。

アイコンの色を変更する方法

よくある重要なケースの 1 つである色について説明しましょう。アイコンがスプライトに入るスクリプトを作成しましたが、このアイコンはプロジェクト全体で異なる色を持つことができます。


<svg/>要素だけでなく、 pathcirclelineなども fill 属性やストローク属性を持つことができることに留意する必要があります。 currentcolorという非常に便利な CSS 機能が役に立ちます。


このキーワードは、要素の color プロパティの値を表します。たとえば、 background: currentcolorである要素にcolor: redを使用すると、この要素の背景は赤になります。


基本的に、すべてのストロークまたは塗りつぶしの属性値をcurrentcolorに変更する必要があります。それが手動で行われているのを見ないことを祈ります、へー。また、SVG 文字列を置換または解析するコードを作成することさえ、非常に便利なツールsvgoに比べてあまり効率的ではありません。


これは、色だけでなく、SVG からの冗長な情報の削除にも役立つ SVG オプティマイザーです。


svgoをインストールしましょう。

 npm i -D svgo


svgoには組み込みプラグインがあり、そのうちの 1 つは、 currentColor: trueプロパティを持つconvertColorsです。この SVG 出力を使用すると、色がcurrentcolorに置き換えられます。以下は、 svgoconvertColorsの使用法です。

 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 ( viteユーザー向け)

    これは、この記事で作成したばかりのスクリプトを基本的に含むプラグインです。プラグインではcurrentColor置換がデフォルトで有効になっているため、プラグインを非常に簡単にセットアップできます。

     // 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 ( Webpackユーザー向け)

    Vite に切り替えるまでは、この Webpack プラグインを使用していました。ただし、Webpack を使用している場合には、このプラグインは依然として優れたソリューションです。手動で色変換を有効にする必要があります。色変換は次のようになります。

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

レイアウトでの使用法

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 コンポーネントがある

結論

開発の効率化とは、単に時間を節約することだけではありません。それは私たちの創造的な可能性を解き放つことなのです。アイコンの管理などの核心的なタスクを自動化することは、単なる近道ではありません。これは、よりスムーズでインパクトのあるコーディング エクスペリエンスへの入り口となります。そして、そのような日常的な作業にかかる時間を節約することで、より複雑なタスクに集中し、開発者としてより早く成長することができます。