2015年9月14日月曜日

CSSのbackground-imageでSVG画像を表示し、SVG非対応ブラウザではPNG画像を表示する(CSSだけで実現)

どういうこと?

タイトルが長いですが、要するにこういうことです。
  • CSSの background-image プロパティでSVG画像を表示する(背景画像とかCSS spriteとか)
  • SVGに対応していない古いブラウザでは、フォールバック(代替)としてPNG画像を表示する
  • JavaScriptを使わず、CSSのみで完結!
Retinaのような高精細ディスプレイでもぼやけずに画像(イラスト)を表示するには解像度に依存しないSVGを使うのが一番いいのですが、IE8やAndroid 2.x系などの古いブラウザではSVGを認識できず、背景が驚きの白さになってしまいます。そんなブラウザにはPNGを読ませてあげなければいけません。
そのへんをうまい具合に表示する小技の紹介です。

代替画像は別にGIFとかJPGでもいいんじゃね?と思うかもしれませんが、どちらもベクトル画像の表現力が低いのでPNGを使いましょう。

ちょっと前置き

これをimgタグでやる方法はいろいろ開発されていると思うのですが(objectタグを使うとかsvgタグの中に書くとか)、background-image の場合はModernizr使おうず!というのが定説になっている感があります。

そんな中、どうにかしてCSSだけで実現できないかと調べた結果、やり方を見つけたのでドヤ顔で紹介します。

多分日本語でここまで詳しく解説してる記事は他にはないと自画自賛。日本語以外でもないんじゃないかな。あったらごめんなさい。

ちょっと長くなりましたが、熱い想いを感じ取っていただいたところで本題に入ります。

とりあえず見せてよ

百聞は一見にしかず。まずはご覧ください。じゃん。
SVG fallback
わかりやすくするために、SVGとPNGで表示内容を変えています。SVG対応ブラウザでは"SVG"、非対応ブラウザでは"PNG"と表示されているはずです。

ただし、IE9やAndroid 3.xなど一部のブラウザではSVG対応しているのにPNGが表示されてしまいます。
理由はあとで説明しますが、そういうもんだと割り切ってください。

どんなコード?

タネ明かしはこんなかんじ。問題の箇所だけ抜粋します。
先にPNGを指定して、後から linear-gradient とかいうオマジナイで上書きするだけ。簡単。超簡単。

どういうことだってばよ

CSS3の新機能、multiple background-imageとlinear-gradientの2つを組み合わせています。
  • multiple background-image - 複数の画像を重ねあわせて背景画像を合成します。画像をカンマで列挙すると、列挙した順番で合成されます(順番が早いものほど前面)
  • linear-gradient - グラデーション画像を作成します。今まではグラデーション画像を使いたければ実際にそういう画像を用意する必要がありましたが、CSS3では画像を用意せずに自動生成できる機能が用意されました。
ちなみに linear-gradient で生成されたものは「画像」という扱いなので、画像が必要な場所なら background-image に限らずどこでも使えます。

linear-gradient(transparent, transparent) は単に透明な画像を生成しているだけなので、SVGと透明な画像を合成…つまりSVGを普通に表示しているのと同じです。

そしてこの子たちのブラウザサポート状況。
各機能のブラウザサポート状況
ブラウザ multiple background-image linear-gradient 両方 SVG
IE 9+ 10+ 10+ 9+
Firefox 3.6+ 16+ 16+ 4+
Chrome 4+ 10+ 10+ 4+
Safari 3.1+ 5.1+ 5.1+ 3.2+
Opera 11.5+ 12.1+ 12.1+ 10.1+
iOS 3.2+ 5+ 5+ 3.2+
Android 2.1+ 4+ 4+ 3+
Opera Mini 5+ - - 5+
この表からわかるとおり、「multiple background-imageとlinear-gradientを両方サポートしているブラウザはSVGもサポートしている」ので、ここでSVGを指定してやれば最初のbackground-imageがうまいこと上書きされてSVGのみ読み込み・表示してくれます。

どちらか一方でも対応していないブラウザでは、この指定が無視されてPNGが表示されます。

IE9など一部のブラウザでPNGが表示されたのはこういう理由です。お察しください。

おまけ その1

表を見ると、「multiple background-imageに対応しているけどSVGには非対応」というブラウザはFirefox 3.x、Safari 3.1、Android 2.xとかなり古いものばかりです。
そんな化石は切り捨てる!という場合は、linear-gradient をなくしたこんな方法もあります。
さらに簡単になったよ。やったね!IE9やiOS 3.2、Opera MiniでもSVGを表示できるようになったよ。やったね!

そもそもSVG非対応のブラウザ自体が化石みたいなもんなので、これは実質IE8対策です。
IE8も切れるなら何も考えずに background-image にSVGを指定するだけでいいですよ。PNGも multiple background-image もいりません。

おまけ その2

全く別の切り口から、こんなやり方もあります。
これは、同じくCSS3の新プロパティ background-size を画像と同時に指定しています。
"0 0"の部分は background-position の値を指定してください。CSS spriteで表示位置をずらす場合は両方の background プロパティに同じ値を指定する必要があります。

またまたサポート状況。じゃん。
各機能のブラウザサポート状況
ブラウザ background-size SVG
IE 9+ 9+
Firefox 4+ 4+
Chrome 15+ 4+
Safari 7+ 3.2+
Opera 11.5+ 10.1+
iOS 7.1+ 3.2+
Android 4.4+ 3+
Opera Mini - 5+
先ほどの方法では対応できなかったIE9やFirefox4などに対応している一方、iOSやAndroidは割と最近のものしか対応していません。

SVGの需要が高いスマートフォンで対応状況が悪い上に、場合によっては background-position を2度指定する必要のあるこちらの方法はあまり使えない感がありますが、せっかく調べたので紹介しておきます。

Sassのmixinを使えば2回指定の部分はあまり面倒じゃなくなるんですけどね。

まとめ

background-image でSVG/代替PNGを指定する方法を3つほど紹介。
ただし、どれも(W3Cの仕様に基づくものではなく)SVGとCSS3新機能のブラウザ対応時期の差を利用したハックなので、ブラウザによってはSVGに対応しているのにPNGが表示されてしまうことがあります。

それでも画像自体は表示されるので実用上の問題はありませんが、必要以上のPNG使用を家訓で禁止されている方、医者に止められている方、SVG教の信者の方などはやっぱりModernizr使うしかありません。

ちなみに…

このテクニックはQiNeelでも使っています。最初の方法でね。

参考


0 件のコメント:

コメントを投稿