2012年7月30日月曜日

svgを使って複数の画像を一つにまとめる方法

svgの文書構造を応用すると,複数の画像や図形を一つのファイルにまとめることができます.このまとめ方は複数存在していて,それぞれ使い方による向き不向きが有るため,その特徴について考えてみましょう.

※set要素を追加

ここでの画像のまとめ方とは,「1・1つのファイルに複数の画像データを含めて」,「2・後から何らかの方法で単体の画像を抽出する」といった事を指すものとします.図で表すとこのような形です.


1の条件からまとめる対象のラスタ画像は前もってdataスキーム形式に変換しておき,svgにテキストデータとして埋め込みます.なおバイナリデータをbase64形式に変換することで,データのサイズがある程度膨らんでしまうのは大目に見て下さい.

svgを用いた画像のまとめ方には大体次のパターンがあります.
  1. symbol要素によるもの
    symbol要素で画像・図形に名前を付けておき,use要素でその画像を参照する方法.
  2. :target擬似クラスによるもの
    指定の図形要素のみdisplay:inlineを指定して表示させるもの.
  3. view要素によるもの
    予め画像や図形を並べておいて,view要素を指定して切り出すもの.
  4. cssスプライトのsvgへの応用
    3と同じく画像を並べておき,background-positionで描画範囲を切り出すもの.
  5. メディアクエリーによるもの
    svgの描画サイズに応じて自動的に内容が切り替わるようにしたもの.
  6. set要素によるもの
    アニメーション機構を使ってスタイルを切り替えるもの.
何れも後から単一の画像を取り出す部分に特徴があります. 以下,それぞれについて見てみましょう.

    1.symbol要素によるもの

    svg文書にはまとめたい画像毎にsymbol要素を作り,その中にimage要素を定義して画像を埋め込みます.html側で画像を取り出すにはインラインsvg要素を宣言し,その中でuse要素を使ってsvg文書を参照させるようにします.
    例)<use xlink:href="sample1.svg#image"/>

    インラインsvgでのみ有効な方法であり,object要素やimg要素から画像を参照することは出来ませんが, use要素で参照している画像がsvg図形であった場合は,html側でスタイルを変更することができるといった特徴を持ちます

    インラインsvgの持つhtmlのcolorプロパティをsvg図形のfill色,stroke色に引き継ぐことができるなど,設計如何によっては非常に柔軟な対応が可能となります.どちらかというと,アイコンフォントの代替のような使い勝手です.

    詳しくはこちらのページを参照して下さい.


    2.:target擬似クラスによるもの

    svgがcssに対応していることから,:target擬似クラスを使って画像の表示・非表示を切り替えてしまおうというやり方です.

    svg文書にはまとめたい画像毎にg要素を作り,その中にimage要素を定義して画像を埋め込みます.このg要素にはhtmlからurlハッシュ値を介して画像を選択できるようにするため,id属性を設定します.

    またg要素に対してstyle要素においてdisplay:noneを宣言し,画像全体を隠します.このままだと何も表示されないので,g要素の:target擬似クラスにdisplay:inlineを設定し,表示ターゲットとなった画像のみが表示されるようにします.

    html側では画像のソースを指定する際,画像のidを合わせて指定するようにします.
    例)<img src="sample2.svg#image"/>
    ※インラインsvgであればimage要素から参照する.
    ※gackground-imageであれば「background-image:url(sample2.svg#image)」とする.

    このやり方では,参照先の図形のスタイルを変更することが出来なかったり,使い方やブラウザによって動作が異なるなど,使い勝手はあまり良く有りません.


    3.view要素によるもの

    svgにはviewBox属性と呼ばれる画像の描画範囲を定義するためのプロパティが設定されています.view要素はこのviewBoxを予め複数パターン定義しておくためのものです.
    この機能を応用し,svg文書内部で複数の画像を予め並べておき,urlで画像の描画領域を切り替えるのです.

    これは一般的に用いられているcssスプライトテクニックのsvgへの応用とも言えるもので,background-positonとbackground-sizeによる画像の抽出処理にsvgの機能を使ったものと言えます.

    なお,使い方は2番の方法と全く同じで,選択したい画像に対応するview要素のidをハッシュに指定するようにします.
    例)<img src="sample3.svg#image"/>



    注意すべき点も2番の方法と全く同じです.なお,svgにはview要素を使わずとも後からviewBoxを自由に設定するための機能が定められています.svgViewで調べてみましょう.
    例)<img src="sample3.svg#svgView(viewBox(0,0,100,100))"/>



    4.cssスプライトのsvgへの応用

    一般的なcssスプライトでは複数の画像をpng画像にまとめ,それをcssのbackground-position,background-sizeプロパティを使って画像を切り出します.この画像をまとめる部分をsvgで行なってしまおうというものです.svgでは画像毎にimage要素を割り当てることができるので,後から中身を変えると言ったことも簡単に出来ます.

    使い勝手はこれまでどおりであり,最も汎用性が高いと言えるでしょう.その一方で,画像の位置を設定するのが面倒であるなど,欠点をも引き継いでしまっています.それでもpng画像のファイルサイズを最適化するために,無理矢理矩形範囲に画像を詰め込むといった必要はなく,そのまま画像を1列に並べるだけで済むといったsvgを利用することによるメリットも存在します.


    5.メディアクエリーによるもの

    この方法はこれまでとは毛色が異なり,svgの描画範囲に応じて出力する画像を切り替えたいといった場合に利用します.詳細な考察についてはここでは省きますが,憶えておいて損はない機能です.


    6.set要素によるもの

    svgのアニメーション機構は任意のスタイル属性の値を変更することができます.このアニメーションはurlから実行することが可能であるため,これまでと同様にdisplay属性を切り替えることで画像の抽出が可能となります.

    set要素にidを指定すると,urlのハッシュ値からアニメーションの実行を行うことができます.


    実際にやってみると判るのですが,余り結果が芳しく有りません.やなりアニメーション機構を適用するのはちょっと無理があるようです.

    動作の検証

    ここまでいろいろなパターンについて見て来ましたが,一方でhtmlからsvg文書を読み込む方法としては次のようなものが考えられます.
    1. インラインsvg要素を用いる.
    2. iframe/object/embed要素を用いる.
    3. img要素(input[type:image])を用いる.
    4. background-imageプロパティを用いる.
    ここで4つに分類したのには訳があり,どのブラウザでもsvgの動作は概ね上記4つのパターンに集約できるためです.(もちろん細かい動作の違いは発生します.)
    以下に動作検証のための一覧表を用意したので,実際の動作について確認してみましょう.2つの画像が切り替わっていれば正しく動作しています.


    • firefox
      何れにおいても正しく動作しているようです.
    • chrome
      現在のバージョンではsvgの動作が不安定なので検証のしようがない.
      (経験上,それほど悪い動作はしていなかったはず)
    • opera
      firafoxについで動作するパターンが多いようです.
    • safari/ie…未調査.
     概ね,symbol要素をインラインsvgで利用したケースとcssスプライトパターンを用いた場合がブラウザに依存せず利用できそうです.set要素用いた場合は結果がよく有りません.何れにせよブラウザのバージョンが上がった際にどのような変化があるかは,実際に試してみないことには分からないので注意して下さい.

    クロスドメインでの確認

    これまではhtmlとsvgが同一のドメインに存在した場合の検証でした.ですが,画像ファイルとhtmlの位置が異なると言ったケースはままあります.ではクロスドメインでの画像の読み込みについて確認してみましょう.




    symbol要素による図形の参照以外は概ね良好に動作しています.なお,これはラスタ画像がsvgに埋め込まれているので上手く行っているのです.svgが外部の画像ファイルを参照していた場合は,正しく動作するとは限りません.



    サンプルコード


    1.symbol要素によるもの

    svg文書にはwidth,height,viewBox属性を定義する必要はありません.
    <?xml version="1.0" standalone="no"?>
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
        <symbol id="image" viewBox="0 0 100 100" preserveAspectRatio="none">
            <image x="0" y="0" width="100" height="100" xlink:href="data:image/png;base64,iV…"/>
        </symbol>
        <symbol id="star" viewBox="-50 -50 100 100" preserveAspectRatio="none">
            <polygon points="0,-40 -24,32 38,-12 -38,-12 24,32"/>
        </symbol>
    </svg>

    2.:target擬似クラスによるもの

    画像のサイズ及びviewBoxは適宜調整するとよいでしょう.
    <?xml version="1.0" standalone="no"?>
    <svg width="100px" height="100px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" preserveAspectRatio="none">
     <style type="text/css">
    g{
     display:none;
    }
    g:target{
     display:inline;
    }
     </style>
     <g id="image">
      <image x="0" y="0" width="100" height="100" xlink:href="data:image/png;base64,iVB…"/>
     </g>
     <g id="star">
      <polygon points="0,-40 -24,32 38,-12 -38,-12 24,32" transform="translate(50,50)"/>
     </g>
    </svg>

    3.view要素によるもの

    取り出したい画像の分だけveiw要素を定義しておくのがミソです.
    <?xml version="1.0" standalone="no"?>
    <svg width="100px" height="100px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
     <image x="0" y="0" width="100" height="100" xlink:href="data:image/png;base64,iVB…"/>
     <view id="image" viewBox="0 0 100 100"/>
     <polygon points="0,-40 -24,32 38,-12 -38,-12 24,32" transform="translate(150,50)"/>
     <view id="star" viewBox="100 0 100 100"/>
    </svg>

    4.cssスプライトのsvgへの応用

    画像を並べてそのサイズを画像のコンテナとなるsvg要素に設定しておきます.
    <?xml version="1.0" standalone="no"?>
    <svg width="200px" height="100px" viewBox="0 0 200 100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
     <image x="0" y="0" width="100" height="100" xlink:href="data:image/png;base64,iVB…"/>
     <polygon points="0,-40 -24,32 38,-12 -38,-12 24,32" transform="translate(150,50)"/>
    </svg>

    5.メディアクエリーによるもの

    <?xml version="1.0" standalone="no"?>
    <svg width="50px" height="50px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" preserveAspectRatio="none">
     <style type="text/css">
    #image{
        display:none;
    }
    @media (max-width: 50px){
        #star{
            display:none;
        }
        #image{
            display:inline;
        }
    }
     </style>
     <g id="image">
      <image x="0" y="0" width="100" height="100" xlink:href="data:image/png;base64,iVB…"/>
     </g>
     <g id="star">
      <polygon points="0,-40 -24,32 38,-12 -38,-12 24,32" transform="translate(50,50)"/>
     </g>
    </svg>

    6.set要素によるもの

    set要素にidを設定しておきます.
    <?xml version="1.0" standalone="no"?>
    <svg width="100px" height="100px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" preserveAspectRatio="none">
     <image x="0" y="0" width="100" height="100" xlink:href="data:image/png;base64,iVB…" display="none">
      <set id="image" attributeName="display" to="inline" begin="indefinite"/>
     </image>
     <polygon points="0,-40 -24,32 38,-12 -38,-12 24,32" transform="translate(50,50)" display="none">
      <set id="star" attributeName="display" to="inline" begin="indefinite"/>
     </polygon>
    </svg>

    0 件のコメント:

    コメントを投稿