12周年記念パーティ開催! 2024/5/10(金) 19:00

実践、SVGスプライト 前編 SVGアイコンの実装手法

SVG非対応のブラウザのシェアが減少し、Web開発にSVGを採用することができるようになってきました。SVGの特性を最大限に活かしたアイコンの実装を解説します。

発行

著者 小山田 晃浩 フロントエンド・エンジニア
実践、SVGスプライト シリーズの記事一覧

はじめに

SVGが利用できないブラウザのシェアが減り*、Webの開発シーンでSVGを採用する機会が増えてきたのではないでしょうか。

*注:SVGが利用できないブラウザのシェア

SVGが利用できない代表的なブラウザはIE8でした。IE8はベンダーであるMicrosoftがサポートしないブラウザとなりました。

SVGはその特性*から、ロゴや地図などの埋め込みに適しています。また、Webページのアクセントとして用いられるアイコンもSVGの有効な活用方法のひとつです。

*注:SVGの特性

SVGの基本的な特性や知識に関しては、次のシリーズなども参考にしてください。

アイコンの埋め込みというとFont Awesomeをはじめとするアイコンフォントが活躍してきました。しかしそれも過去の話になるかもしれません。SVGが使いやすい環境が整ってきたことにより、アイコンはフォントで埋め込むのか、それともSVGで埋め込むのかを改めて考えるタイミングがきています。

私はSVGを使ってアイコンを埋め込むさまざまな方法を試行した後、スプライトによる埋め込み方法が、汎用性があり扱いやすいと判断しました。その考えに至るまでの道のりと、スプライトで埋め込む方法を解説します。

なお、紹介するサンプルは次のリポジトリからダウンロード、またはクローンできます。併せて参照してください。

実践、SVGスプライト サンプルリポジトリ

これまでのアイコン埋め込み

アイコンの埋め込みはこれまで、PNG画像のようなビットマップ画像のスプライト、アイコンフォントといった手法が考案され、私たちはこれを利用してきました。それぞれの特徴をおさらいしてみましょう。

特徴 ビットマップ画像スプライト アイコンフォント
フルカラー
スケーラブル
1ファイルにパック
CSSでの編集

ビットマップ画像スプライトは、フルカラーという利点がありますが、ビットマップであるがゆえに拡大縮小に弱く、マルチスクリーン対応をするシーンでは、これが大きな欠点となります。

一方でアイコンフォントは、フォントがベクターデータであるため拡大縮小に強く、またCSSのcolorfont-sizeを利用することで、色や大きさのバリエーションを用意できますし、:hover擬似クラスとの相性がいいのも特長です。ただし単色という欠点があります。フルカラーが求められるアイコンなどには不向きです。

SVGでアイコンを埋め込む

SVGはスケーラブルかつフルカラーであるという特長があります。つまりビットマップ画像スプライトやアイコンフォントの欠点を補うことができます。ではどのようにアイコンを埋め込めばいいのでしょうか。

SVGの、特にアイコン向けの埋め込みは3つの方法が知られています。

  • インライン埋め込み(テンプレートエンジンと併用)
  • インライン埋め込み(URI参照)
  • スプライト

順に解説します。

インライン埋め込み(テンプレートエンジンと併用)

GitHub.comでのアイコンは、インラインでSVGを直接埋め込む方法をとっています。この方法はSVGの恩恵をすべて享受することができます。埋め込まれたアイコンはフルカラー、スケーラブル、そしてCSSで編集も可能です。

一方で、インラインによる埋め込みは、テンプレートエンジンが利用できない場合には不向きです。GitHubの技術ブログで紹介されていた例を見てみましょう。

GitHubはRuby on Railsで運用されており、HTMLはテンプレートエンジンにより出力されています。この中にアイコン埋め込み用の独自ヘルパーが用意されています。

<%= octicon(:symbol => "plus") %>

上記のヘルパーをテンプレート内に記述すれば、インラインでSVGのソースコードが展開される仕組みが用意されています。

<svg aria-hidden="true" class="octicon octicon-plus" width="12" height="16" role="img" version="1.1" viewBox="0 0 12 16">
  <path d="M12 9H7v5H5V9H0V7h5V2h2v5h5v2z"></path>
</svg>

この方法は、あらゆるWeb開発環境に応用できるというわけではなく、テンプレートエンジンの利用が前提です。残念ながら汎用性に欠けてしまいます。この方法が容易に使えるようになるのは、Web Componentsが一般に広く普及した、もう少し未来のこととなるでしょう。

インライン埋め込み(URI参照)

SVGには「一度実体を定義しておけば、いつでもそれを呼び出せる」機能が備わっています。呼び出しにはuse要素*を利用します。

*注:use要素

use要素はSVGの仕様で定義されている要素です。

次のコード例では、defs要素内(HTMLでいうhead要素)にredCircleというidのシンボルを定義し、これをuse要素で座標を変えながら3回呼び出しています。

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="128" viewBox="0 0 256 128">

<defs>
  <symbol id="redCircle" viewBox="0 0 20 20" >
    <circle cx="10" cy="10" r="10" fill="red"/>
    <text x="0" y="10">Circle</text>
  </symbol>
</defs>

<use x="20" y="20" width="20" height="20" xlink:href="#redCircle"/>
<use x="50" y="50" width="20" height="20" xlink:href="#redCircle"/>
<use x="80" y="80" width="20" height="20" xlink:href="#redCircle"/>

</svg>

この仕組みはアイコン埋め込みに応用することができます。上記では単独のSVGドキュメント内で#redCircleを参照していますが、これを応用して、HTMLから、外部にあるSVG内のidを参照することもできます。

HTML側ではuse要素を使って、demo4.svgという外部ファイル上にあるSVGのオブジェクトを呼び出しています。

<!-- 省略 -->
<style>
svg.example4{
  width: 20px;
  height: 20px;
}
</style>
</head>
<body>


円アイコン

<svg class="example4">
  <use xlink:href="demo4.svg#redCircle"/>
</svg>

四角アイコン

<svg class="example4">
  <use xlink:href="demo4.svg#blueRect"/>
</svg>

</body>
</html>

「use要素を利用した外部SVGのインライン埋め込み」のデモは、次のように記述されています。

<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128">

  <defs>
    <symbol id="redCircle" viewBox="0 0 20 20" >
      <circle cx="10" cy="10" r="10" fill="red"/>
      <text x="0" y="10">Circle</text>
    </symbol>

    <symbol id="blueRect" viewBox="0 0 20 20" >
      <rect width="20" height="20" fill="blue"/>
      <text x="0" y="10">Rect</text>
    </symbol>
  </defs>

</svg>

上記のコードを見てわかるように、ひとつのsvgファイルに複数の実体を格納することができ、これにより「1ファイル内に複数のアイコンをパック」という、ビットマップ画像スプライトやアイコンフォントと同じ特長を実現することができます。

一方で、HTMLのソースコードを見てみると、呼び出すアイコンのパスをxlink:href属性に毎回属性値として設定する必要があります。

そのためSVGならではの利点はありますが、SVGファイルへのパス情報が毎回HTMLのコードに入り込むことになり管理しやすいとはいえません。(もしSVGアイコンのファイル名や配置ディレクトリの変更をする場合、全てのHTMLを再編集しなくてはいけません。)

また、IE9〜11においてSVGのURI参照はサポートされているものの、あくまでも同一ファイル内でのみであり、残念ながら上記で紹介したファイル間を越えたSVGのURI参照は無効となってしまいます。

ポリフィルを利用することで、擬似的にこの制限を突破することができますが、スマートな方法とは言えません。

GitHubの例で紹介されていた「テンプレートエンジンを利用したSVGインライン埋め込み」よりは汎用性がありますが、それでもいくつかの制限があります。

スプライト

私が最後にたどり着いたのはSVGのスプライトでした。この方法は汎用性があり、かつビットマップ画像スプライトやアイコンフォントの欠点を穴埋めできる特性があります。また、あまり知られていませんが、CSSを工夫することでアイコンサイズの自由度を向上することができます。

さっそく例を見てみましょう。次の例ではfont-sizeが引き継がれて、アイコンの大きさに反映されていることが確認できます。

上記でアイコンとして呼び出されているスプライトシートは、次のSVGファイルです。ファイルのサイズは24KBで、gzip圧縮が効けば、さらに容量は小さくなります。一方で、仮にPNGファイルで同様の画像を用意した場合には、40KB近いバイナリファイルとなります。

CSS内ではアイコンサイズをem%で管理することにより、フォントサイズに合わせアイコンサイズの自動調整が可能になります。

.icon{
  width:  1em; /* ( 横幅(px) / 高さ(px) ) */
  height: 1em; /* ( 高さ(px) / 高さ(px) ) */
  display: inline-block;
  vertical-align: middle;
  background-image: url(".SVGSprite.svg");
  background-repeat: no-repeat;
  background-size: 500% 400%; /* (SVG全体の横幅 / width * 100%) (SVG全体の高さ / height * 100%) */
}

/* background-position: ( 横オフセット(px) / 高さ(px) ) ( 縦オフセット(px) / 高さ(px) ) */
.icon.icon1 { background-position: 0em  0em; }
.icon.icon2 { background-position: 0em -1em; }
.icon.icon3 { background-position: 0em -2em; }

この方法にも弱点はあります。SVGのスプライト配置や、background-positionの値設定を人の手で行うのが負担である点です。ただ、これはgulpとSassを使うことで自動化できます。これについては、次回詳しく解説する予定です。

まとめ

今回はWeb開発の現場で、SVGの採用が以前よりもぐっと楽になったこと、また用途としては、アイコンの実装にも向いていることを述べました。

さらに、SVGを用いたアイコン実装の方法を3つ紹介しました。その方法の中でもSVGスプライトを作成する方法は、フレキシビリティが高いことがおわかりいただけたと思います。ただ、手動でこれを行うのは負担が大きいと言えます。

次回はSVGスプライトの作成を、gulpとSassを組み合わせて自動化する方法を解説します。少し手を入れるだけで、実務ですぐに使えるソースコードも併せて紹介します。