1つのSVGスプライト画像をCSS背景画像とインラインHTMLの両方で読み込む方法

Advertisement

この記事は「SVG Advent Calendar 2015 」の9日目の記事です。初めてAdvent Calendar的なものに参加させてもらいます。よろしくお願いします。

※Advent Calendarの締め切りがあるので、検証途中ですが公開しちゃいます!ご了承ください。現段階では、旧ブラウザをサポートするためのフォールバックが最後まで検証できていないです。あとで追記します。

—–ここから本文—–

そろそろロゴとかアイコンまわりの画像をSVGに移行したいと思い、Gulpを使って複数のSVGアイコンを1つにまとめるアイコンシステムを検証してこのブログでも紹介しました。でも、Gulpの設定に慣れていないと難しいし、CSSで背景画像として読み込めないというデメリットもありました。

そこで、今回はロゴやアイコンなどの複数の画像を、Sketchでスプライトにまとめて1つのSVG画像として保存して、CSSの背景とインラインHTMLの両方で使う方法を検証してみます。この方法だとGulpなどのビルドツールがいらないので、コマンドラインツールが使えない制作環境でもワークフローにそのまま組み込めます。また、ロゴやアイコンが一つのファイルにまとまっているのでメンテが楽になりそうです(納品後に自分で更新したいクライアントにも良いかも)。

矢印など、テキストの補完に使う画像はCSSの背景画像で、ロゴやアイコンなどビジュアルのみでなくコンテンツとして扱うものはインラインで画像として記述するのがセマンティックで理想的です。フォールバックを含め、そう言った対応ができるのか検証していきます。

結論から先に書いてしまうと、Microsoft Edge以外は、フォールバック対応を施すことでなんとか対応できそうです。これだったら、もう少し検証すれば、プロダクション・レベルでも使えるように思います。

SVGスプライト画像の準備

今回はSketchを使ってSVGスプライトを作成します。インラインHTMLとして使う際に画像をIDで参照するために、レイヤーに名前をつけて、複数の図形からなるものはグルーピングしておきます。

最終的なスプライト画像

ここではPNG画像を表示させてます。実際のSVGはこちら 。あと、参考までにSketchファイルはこちら からどうぞ。

Sketchのレイヤー構造

Sketchのレイヤー構造は以下のようになってます。

Sketchが書き出すSVGについて

上の画像をSVGとして書き出すと、以下のような構造で書き出されます。
※構造に関係ない部分は省略してあります。

<svg width="400px" height="300px" viewBox="0 0 400 300">
  <g>
    <g id="sprites">
      <rect id="orange" fill="#F6A623" x="170" y="10" width="60" height="60"></rect>
      <rect id="red" fill="#D0011B" x="90" y="10" width="60" height="60"></rect>
      <rect id="brown" fill="#8B572A" x="10" y="10" width="60" height="60"></rect>
      <g id="logo" transform="translate(10.000000, 89.000000)">
        <!-- 図形部分 -->
      </g>
      <g id="icon-plus" transform="translate(10.000000, 190.000000)">
        <!-- 図形部分 -->
      </g>
      <g id="icon-minus" transform="translate(59.000000, 190.000000)">
        <!-- 図形部分 -->
      </g>
      <rect id="bound" stroke="#979797" stroke-width="2" x="1" y="1" width="398" height="298" rx="10"></rect>
    </g>
  </g>
</svg>

要点は以下のとおりです

  • グルーピングしたものは<g>で括られる
  • レイヤー名がそのままID名として使われる

余談:symbol要素とg要素の違い

SVGスプライトのGulpワークフローの時は、<symbol>を使ったSVGを書き出しましたが、今回のスプライトでは<g>を使っています。g要素は論理的に図形をグルーピングして描画対象としてそのまま表示されるのに対し、symbol要素は図形をグルーピングするところまでは一緒ですが、そのままでは描画対象にはならず、<use>を使って参照しないと表示されません。

CSS Backgroundとして読み込む方法

基本的にはCSSスプライトの画像をSVGにするだけです。たとえば、スプライトのX座標が58、Y座標が189にある(-)アイコンを表示するには以下のように記述します。

HTML

<span class="css-icon icon-minus"></span>

CSS

.css-icon {
  display: inline-block;
  background: url(svg/sprites.svg) no-repeat;
  background-size: 400px 300px;
}
.icon-minus {
  width: 31px;
  height: 31px;
  background-position: -58px -189px;
}

Inline HTMLで読み込む方法

同じSVGスプライト画像をインラインHTMLで参照するには以下のようにHTMLを記述します。

<svg width="60" height="60">
  <use xlink:href="svg/sprites.svg#brown" x="-10" y="-10" />
</svg>

上のようにsvgに幅と高さを指定して、useでX座標とY座標を調整しても良いですし、以下のように、svgにviewBoxを指定する方法もあります。

<svg width="320" height="80" viewBox="10 90 320 80">
  <use xlink:href="svg/sprites.svg#logo" />
</svg>

これで、モダンブラウザであれば、とりあえず1つのSVGプライト画像をCSSの背景画像とインラインHTMLで読み込むことができました。

デモはこちら

ある意味ここからが本題ですが、以下でブラウザサポートについて検証していきます。

ブラウザサポート

例えばロゴのような画像の場合、さすがに表示なしというわけにもいかないので、SVGがサポートされていないブラウザ向けにフォールバックの対応が必要です。ただ、フォールバックのための作業を極力シンプルにするために、スプライト画像をPNGで保存して全てのフォールバックに使えたら理想的なので、それを目標に検証していきます。

まず、caniuse.com でチェックするとサポート状況は以下のようになっています。

SVG in CSS backgrounds

  • Chrome 5以上
  • iOS Safari 4.3以上
  • Safari 5以上
  • Android Browser 3以上
  • IE 9以上
  • Firefox 24以上

Inline SVG in HTML5

  • Chrome 7以上
  • iOS Safari 5.1以上
  • Safari 5.1以上
  • Android Browser 3以上
  • IE 9以上
  • Firefox 4以上

SVG fragment identifiers

  • Chrome 36以上
  • iOS Safari 5.1以上
  • Safari 8以上
  • Android Browser 46以上
  • IE 9以上
  • Firefox 15以上

他の2つはまだしも、ここのサポートが難しいですね。Android Browser 4.xでもサポートされてないのがちょっと。。。

Advertisement

フォールバック

以下の対応が必要になります。

  1. CSSの背景画像としてSVGを利用する場合のフォールバック
  2. SVGをインラインHTMLで記述する場合のフォールバック
  3. インラインHTMLで記述し外部SVGファイルを参照する場合のフォールバック
  4. SVGの一部を参照するfragment identifierを使う場合のフォールバック

例によって、CSS-TricksのA Complete Guide to SVG Fallbacks という記事で、様々なフォールバックの方法が詳細にわたって解説されていますので、より詳しく知りたい方はそちらをご覧ください。

以下、今回のSVGスプライト用に必要なフォールバックをまとめてみます。

CSSの背景画像としてSVGを利用する場合のフォールバック

CSSを以下のように修正することで、サポートされていないブラウザではPNG画像を読み込ませることができます。

修正前

.css-icon {
  display: inline-block;
  background: url(svg/sprites.svg) no-repeat;
  background-size: 400px 300px;
}

修正後

.css-icon {
  display: inline-block;
  background: url(svg/sprites.png) no-repeat;
  background: url(svg/sprites.svg) no-repeat, linear-gradient(transparent, transparent);
  background-size: 400px 300px;
}

SVGをインラインHTMLで記述する場合のフォールバック

Modernizer でInline SVGのサポートをチェックして、フォールバックとしてsvg要素の背景画像としてPNG画像を読み込ませます。CSSの背景画像のフォールバックで使ったPNG画像をそのまま使えるようにします。

Modernizerを使うと、Inline SVGをサポートしないブラウザでbodyタグにno-inlinesvgを挿入してくれます。それを利用して、CSSにno-inlinesvg用のスタイルを追加してHTMLにもIDを記述します。

HTML

svg要素にclassを追加します。

<svg class="in-svg in-svg-brown" width="60" height="60">
  <use xlink:href="svg/sprites.svg#brown" x="-10" y="-10" />
</svg>

IE8でsvgを表示させるためにhead内に、以下を記述します。

<!--[if lte IE 8]>
<script>document.createElement("svg");</script>
<![endif]-->

CSS

CSSの背景画像としてSVGを使うために記述したCSSをそのまま活用して、フォールバック用に同じスタイルを適用するためにCSSにclassを追加します。

.css-icon,
.no-inlinesvg .in-svg {
  display: inline-block;
  background: url(svg/sprites.png) no-repeat;
  background: url(svg/sprites.svg) no-repeat, linear-gradient(transparent, transparent);
  background-size: 400px 300px;
}

.icon-minus,
.no-inlinesvg .in-svg-icon-minus {
  width: 31px;
  height: 31px;
  background-position: -58px -189px;
}

これでSVGのインラインHTMLでの使用をサポートしていないIE8以下でもフォールバックのPNG画像が表示されるようになります。仮想マシンですけど、Microsoft Edge Dev でダウンロードしたVirtualBox向けのIE8 on Win7と通常ライセンスのWindows XP ProfessionalのIE6で確認できました。

デモはこちら

これで昨晩はIE8でも表示されたのを確認できたと思ったのですが、もう一度やってみたら表示されません。。。

と、ここまでで時間切れです。。。すみません、ここまでしか検証できていません。

せっかくAdvent Calendarに参加させていただいてるので、ここまでで公開させて持って、続きは、必ず後追いで検証して、追記したいと思います。あと少しのところまで来ていると思うのですが。。。

では、To be continued…

SVGをインラインHTMLで記述し、外部ファイルをfragment identifierを使って参照する場合のフォールバック

ちょっとややこしいですけど、IE9+ではインラインHTMLでuse要素を使って外部SVGファイルを読み込んで#hogehogeというfragment identifierを使って画像を表示させる方法をサポートしていません。そのため、SVG Icon Sprite Polyfill というポリフィルを使ってフォールバックします。

このポリフィルでは、use要素を使って参照している外部SVGファイルのコンテンツをXMLHttpRequestを使ってドキュメントに挿入しています。

Githubでは「JSファイルを設置するだけ」と書いてあるのですが、それだとSVGがページ上部に表示されてしまいます。このポリフィルではXMLHttpRequestでSVGをドキュメントに挿入する際にsvg-poly-targetというIDを付与するので、以下のCSSを追加して対応しました。

#svg-poly-target { display: none; }

JSファイルは、bodyの最後で読み込ませるようにしました。

<script src="js/svg-icon-polyfill.min.js"></script>

これで、仮想マシンのIE9とIE11で画像が表示されるのが確認できました。

デモはこちら

あとはMicrosoft Edge…

これで、モダンなIE以外のブラウザではSVGでスプライトを表示、また、サポートがないブラウザではPNG画像を表示するように対応ができたはずです。あとは、Microsoft Edgeのみになります。最新のMicrosoft Edgeでは外部ファイルの読み込みをサポートするようになったようなのですが、Microsoft Edge Dev からダウンロードしてきたVirtualBox用のMSEdge on Win10では確認できていません。

動作確認をしたブラウザ

以下のブラウザで動作を確認できました。

  • Chrome 47 (Mac)
  • Firefox 42 (Mac)
  • Safari 9.0.1
  • Mobile Safari 9 (iOS 9.2 / iPhone 6s実機)
  • Chrome Mobile 47 (Android 6.0 / Nexus 5実機)

仮想マシン(VirtualBox)

  • Chrome 47 on Win10
  • Firefox 42 on Win10
  • IE11 on Win7 (Edge Dev版)
  • IE9 on Win7 (Edge Dev版)
  • IE8 on Win7 (Edge Dev版)
  • IE6 on WinXP Professional

他のSVG関連の記事

こちらもご参考までにどうぞ:

“1つのSVGスプライト画像をCSS背景画像とインラインHTMLの両方で読み込む方法” への2件のフィードバック

  1. モンモン より:

    こんばんは!
    今度のプロジェクトでアイコン周りをSVGスプライトにしてメンテナンスしていこうかと考えているのですが、CSS背景画像でbackground-positionの座標値は手動で調べて記述しているのでしょうか?gulpで自動抽出するnpmとかあればいいのですが…

    • ryo より:

      コメントありがとうございます。
      Sketchなどでスプライト画像を管理するなら、手動でもそれほど手間ではないかなと思ったので手動でやりました。

      書き出したSVGを読み取ってCSSを書き出してくれるgulpプラグインがあったら便利ですね。ただ、そう言ったプラグインは見つけられてないです。

      以下のプラグインあたりが近そうですが。。。
      ざっと見たところ、違うサイズのSVG画像がサポートされているのか不明ですが、複数のSVG画像を1つにまとめて、CSSも書き出してくれるみたいです。
      https://github.com/jkphl/gulp-svg-sprite

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です