見出し画像

インラインSVG効率化テクニック

こんにちは、AQUARING かに です。

ここ最近はつぶやきProcessingネタの記事ばかりになってしまっていたので、たまにはフロントエンドネタの記事を書こうと思います。

今回はHTMLにSVGタグをインラインで埋め込む際の効率化について紹介します。

インラインSVGについて

基本的には単色アイコンの場合はアイコンフォント化してしまう方がいいので、インラインSVGを採用する際の条件としては「複数色で色のバリエーションがある」または「パーツごとにアニメーションさせたい」場合になります。
なので今回は以下の複数のパーツを持つアイコンを例に説明していきます。

複数のパーツの星のアイコンSVG


インラインSVGに必要な記述のみにする

イラストレーターでSVG形式で保存すると以下のようにコメントやSVGファイルとして閲覧する際に必要な属性値などが付与されています。

<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
<svg version="1.1" id="レイヤー_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
	 y="0px" viewBox="0 0 40 40" style="enable-background:new 0 0 40 40;" xml:space="preserve">
<style type="text/css">
  .st0{fill:#FFC61B;}
  .st1{fill:#FEE187;}
</style>

<!-- 省略 -->

</svg>

htmlにインライン記述する場合はほとんど必要ないため、必要な属性のみ残して他は消してしまいましょう。

<svg class="svg-star" viewBox="0 0 40 40" width="40" height="40">

<!-- 省略 -->

</svg>

残す属性はviewBoxだけでOKですが、imgタグと同じで何もスタイルを当てないとwidth:100%; に広がってしまうので、width, height を追加してviewBoxと同じ大きさにしておきましょう。

.svg-star .st0 { fill: #FFC61B; }
.svg-star .st1 { fill: #FEE187; }

スタイルはCSSファイルに移動しておきます。

このインラインSVGをそのままコンポーネント化してしまうと、コンポーネントを使うたびに同じSVGの記述が何度もhtmlに出力されることになります。それではhtmlの記述量が無駄に増えてよくないですよね。
そんなときに便利なのが <symbol> です。

SVGシンボルの定義

<svg style="display:none;">
  <defs>
    <symbol id="svg-star" viewBox="0 0 40 40">
      <path class="st0" d="..." />
      <polygon class="st1" points="..." />
      <!-- 省略 -->
    </symbol>
  </defs>
</svg>

svg タグの直下に <defs> タグを書き、その中に <symbol> を定義します。
やることは簡単で、さきほどのインラインSVGの <svg> の部分を <symbol>に書き換えるだけです。
<symbol> はテンプレート定義のようなもので、それだけでは実体を持ちません。
あとで参照するために <symbol> には id を設定しておきます。
<symbol> を定義するsvgタグは表示されている必要はないので、display: none; で消しておきましょう。

SVG では、後で再利用できるよう描画オブジェクトを定義します。参照される要素は、可能なかぎりdefs要素内で定義されることが推奨されています。defs要素内でこれらの要素を定義することは、SVG の要素の可読性を向上させ、ひいては操作性をも向上させます。defs要素の描画要素は、そのままでは描画されません。ビューポート上で描画したい場所へそれらの要素を描画するために、<use>要素を使用します。

defs - SVG: スケーラブルベクターグラフィック | MDN

SVGシンボルの使用

<svg class="svg-star" viewbox="0 0 40 40" width="40" height="40">
  <use href="#svg-star" />
</svg>

定義したシンボルを使うときは svg タグの中で <use> の href 属性で <symbol> の id を参照します。
<use> で <symbol> を参照することで、テンプレートのインスタンスを生成して実体化するようなイメージです。

useにしたらスタイルが効かなくなってしまいました

そのまま <symbol>  <use> に書き換えるだけではスタイルが効かなくなってしまうので、ちょっと書き換える必要があります。

スタイルの適用方法

<svg class="svg-star" viewbox="0 0 40 40" width="40" height="40">
  <use class="st0" href="#svg-star"/>
</svg>

DOM構造の変化によって <symbol> 内にある .st0 などのセレクタが機能しなくなったため、 <use> タグに class をつけることでスタイルを適用できます。

スタイルの適用はできましたが、
useで参照したsymbol全体にst0をつけたせいで単色になってしまいました


スタイルの適用方法(パーツごと)

<svg style="display:none;">
  <defs>
    <symbol id="svg-star" viewBox="0 0 40 40" width="40" height="40">
      <g id="svg-star-st0">
        <path d="..."/>
        <!-- 省略 -->
      </g>
      <g id="svg-star-st1">
        <polygon points="..."/>
      </g>
    </symbol>
  </defs>
</svg>

<symbol> <use> にしたうえで複数パーツに別々のスタイルを適用するには、<symbol> の時点でパーツごとに <g> でグループ分けして、それぞれに id をつけ、<use> で個別に参照します。

<svg class="svg-star" viewbox="0 0 40 40" width="40" height="40">
  <use class="st0" href="#svg-star-st0"/>
  <use class="st1" href="#svg-star-st1"/>
</svg>
これで <symbol> <use> 経由でのパーツごとのスタイル適用ができました


パーツごとに分割できてしまえばあとはclassでのカラーバリエーションも作り放題です。

.svg-star.-yellow .st0 { fill: #FFC61B; }
.svg-star.-yellow .st1 { fill: #FEE187; }

.svg-star.-blue .st0 { fill: #1CACFF; }
.svg-star.-blue .st1 { fill: #87D7FF; }

.svg-star.-red .st0 { fill: #FF1C1C; }
.svg-star.-red .st1 { fill: #FF8787; }
カスタムカラーの星アイコンたち


参考リンク

<use> - SVG: スケーラブルベクターグラフィック | MDN
defs - SVG: スケーラブルベクターグラフィック | MDN
<symbol> - SVG: Scalable Vector Graphics | MDN
使用したアイコン


この記事が気に入ったらサポートをしてみませんか?