本記事ではスクリプトレスでこの画像の切り替えを実現させようというもので,svgの使い方のシナリオごとに対処策を探るものです.特に懸案だった背景画像におけるフォールバックが解決したため,ほぼすべてのケースで対処可能となりました.
※01/22補足)この記事はスタイルとともにスクリプトについてもう少し広がりそうなので,別記事としてまとめました.
※01/22記事の内容について検証を行なっています.動作的にはこれで良さそうなのですが,理屈の部分で・・・→解決しました.
※01/27補足)つづきを書きました.
※2014/08/15補足)コメントを追加
※2014/08/16リンクを追加
svgをサポートしないブラウザ
svgはweb上でベクタ画像を表示するもので,webのこれからを担うものとして注目を集めています.しかし,htmlでのサポートが確定したのが比較的最近と言う事もあり,ブラウザによってはsvgを表示することが出来ないものが存在します.例えば次のブラウザです.
- internet exprolerのバージョン8以前のもの
- android osのバージョン2以前の標準ブラウザ
しかし,画像ファイルの利用の仕方には様々なものがあり,一概にこうすれば良いというわけに行かないのが悩ましいところです.
svgの利用シナリオ
htmlページにsvg画像を挿入するには概ね次の4つの方法が挙げられるでしょう.
- インラインsvgの利用
- object要素による表示
- img要素による表示
- background-imageによる表示
「インラインsvgの利用」の場合
そもそもこの方法はhtmlとsvgとを密に連携させることを目的としています.従ってこれをsvgをサポートしない環境から表示することに余り意味がないのですが,ブラウザがsvgをサポートしてないことをスクリーンに表示させたい場合もあるでしょう.
この場合はforeignObject要素を用いることで対処することができます.
この方法は以前拙ブログにて紹介しました.
<!DOCTYPE html>
<html>
<body>
<svg width="200px" height="200px">
<rect x="50" y="50" width="100" height="100" fill="yellow"/>
<foreignObject display="none">
<img src="img.png"/>
</foreignObject>
</svg>
</body>
</html>
このコードをsvgを解釈できないブラウザが読み込むと,中身のimg要素のみが有効となります.一方svgをサポートするブラウザはdisplay=noneを元にimg要素の描画を行いません.
※但しこちら(SVGをIE等のブラウザ対応を考慮して使う方法まとめ(SVGのフォールバック画像など))で指摘されているようにSVGをサポートするブラウザでは必要の無いimg要素が読み込まれてしまう点に注意しましょう.(ieだけなら条件付きコメントが使えるんだけれど)
→解決しました
HTMLパーサーを使ったインラインSVGのフォールバック方法
http://defghi1977-onblog.blogspot.jp/2014/08/htmlsvg.html「object要素/img要素による表示」の場合
この場合はobject要素が標準的にもつフォールバック機構を利用しましょう.img要素でsvgを表示するのはフォールバックが面倒であるためおすすめしません.
<object data="hoge.svg"><img src="hoge.png"/></object>
※セキュリティの絡みからimg要素でsvg画像を表示したい場合は,先ほどの「インラインsvg」 を使ってsvgのimage要素からsvg画像を読み込むようにするとほとんど同じ効果が得られます.
<!DOCTYPE html>
<html>
<body>
<svg width="200px" height="200px">
<image width="100%" height="100%" preserveAspectRatio="none" xlink:href="img.svg"/>
<foreignObject display="none">
<img src="img.png"/>
</foreignObject>
</svg>
</body>
</html>
「background-imageによる表示」の場合
さて,本記事の主題です.上記の方法に比べcssのbackground-imageプロパティからsvg画像を読み込んだ場合,実は非常にフォールバックしにくいと言った問題があります.というのも,フォールバックを実現するには次の要件を充たす必要があるためです.
- svgをサポートしない環境でのみ実行されるスタイルシートを作る
※PCでの閲覧をメインに据えており,androidを無視して良いのであれば次のような方法があります.
- 背景画像でSVG形式の画像が表示できない際にPNG形式の画像を表示する方法(IE7,8用)
backgroundプロパティ内部にrgba関数を挿入すると,レガシーieで内容が無視される事を利用している. - 条件付きコメントによるスタイルの変更
svgをサポートしない環境でインラインsvgはどのように扱われるか?
その前にsvgをサポートしない環境でインラインsvgの挙動について考えてみましょう.
webブラウザは解釈できない要素を発見するとその要素を無視します.更にその中に理解可能な要素があった場合は,何事もなかったように描画処理を続行します.
先ほどのインラインsvgのフォールバック処理時では,svg要素自体は非表示としてあるにもかかわらず,ブラウザがその内容を理解できずに中身のimg要素が表示されることを利用していました.
ではこの仕組みを何とか利用することは出来ないでしょうか?
metadata要素にlink要素を仕込む
ここでsvgの機能をおさらいしておきましょう.metadata要素はsvg画像に任意のユーザーデータを挿入するための仕組みで,通常この要素の配下にある要素はsvgグラフィックの描画を行う際に無視されます.従って,htmlの要素を挿入しても仕組み上は問題ないはずです.
そこで,インラインsvg内のmetadata要素にスタイルシートを参照するlink要素を記述した場合,どのような結果となるでしょう.svgのサポート状況毎に考えてみましょう.コードとしては次のような形を取ります.
<html>
<body>
<svg>
<metadata>
<link href="css.css" rel="stylesheet" type="text/css"/>
</metadata>
</svg>
</body>
</html>
svgをサポートする環境
svgをサポートする環境であれば,metadata要素内部のlink要素は描画処理に関係ないものとして無視されるでしょう.
svgをサポートしない環境
svgをサポートしない環境であれば,svg要素及びmetadata要素の存在が無視され,link要素がそこに存在するものとして扱われるでしょう.通常,link要素はhead要素の下に配置されるべきですが,その部分は目をつむって下さい.
このようにsvgのサポート状況でスタイルシートの切り替えができる事になります.
background-imageにおけるsvgのフォールバック手法
では実際にコードを記述してみましょう.
●メインとなるhtml文書
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<style>
div{
height:100px;
background-image:url("sample.svg");
}
</style>
</head>
<body>
<svg display="none">
<metadata>
<link rel="stylesheet" href="fallback.css" type="text/css"/>
</metadata>
</svg>
<div>
この部分の背景はsvgのサポート状況により切り替わります.
</div>
</body>
</html>
●link要素が参照するスタイルシート.
div{
background-image:url("fallback.png");
}
ポイントは次の通りです.
- メインとなるhtml文書はいつもの通り作成する.背景画像はsvgとしておく.
- svg-metadata-link要素を定義し,link要素から外部cssを参照する.
- svg要素はレイアウトの邪魔とならないようにdisplay="none"を指定しておく.
- 外部cssにはフォールバック画像を指定するスタイルを記述しておく.
このように正しく画像の切り替えが行われていることが判ります.
補足・metadata配下のstyle要素
ではlink要素の代わりにstyle要素を使えばよいではないか?とも考えました.が,試してみると上手く行きません.metadata要素の意味合いから上手くいって良さそうなのですが,metadata要素配下のstyle要素は有効になってしまいます.
ただこの動作はなんとなく説明が付きます.htmlでは名前空間の指定が必要ない代わりに,勝手に名前空間の解決がなされます.従ってmetadata要素配下のstyle要素はsvgのstyle要素として解釈されてしまうのでしょう.このことからmetadata要素は名前空間の指定可能なスタンドアロンのsvgやxhtmlにおけるインラインsvgで利用すべきものと考えられます.
そうではなく,svg要素の内部にlink要素が見つかった場合の動作がたまたま「無視」だったようです.
補足・動作の拠り所
この動作の拠り所となったhtmlの仕様は次の通りです.(多分これでいいはず)
- http://www.w3.org/html/wg/drafts/html/master/syntax.html#parsing-main-inbody
レガシーie,androidの場合
webブラウザが解釈できないsvg要素はレンダリング上無視されますが,その中のlink要素はhead要素にあるものとして解釈されます. - http://www.w3.org/html/wg/drafts/html/master/syntax.html#parsing-main-inforeign
モダンブラウザの場合
htmlとは異なるコンテキストとなるsvg要素内部ではlink要素はhtmlとは関係ないものとして無視されます.(一方,div要素などのhtml要素の一部はsvg要素の外にあるものとして扱われます.)
動作検証を行った環境
firefox,chrome,opera,safari,ie10
ie6,ie8,android2
動作原理上,この他のブラウザでも動作するものと思われます.
0 件のコメント:
コメントを投稿