svg要素の基本的な使い方まとめ

Written by defghi1977@xboxLIVE.この文書は全てテキストエディタで作成しています.えーと,そりゃもうゴリゴリと.

5.図形の定義と再利用

svgはプログラム言語の一つであるため,一般の言語と同様に部品の共通化を行うと言った機能もサポートしている.本項ではsvgを構成する部品の定義を表すdefs要素と,新たな図形を定めるsymbol要素,それらの部品を「使う」use要素について示す.個人的にはsvgを語る上で最も重要な機能だと考えており,これを知ると知らざるとでは作業量に雲泥の差が発生することもありうるので,絶対に押さえておきたい.また,外部の画像をsvgの部品として扱う為のimage要素もここで取り扱う.

defs:定義,use:図形の複製

defs要素は各種定義情報(テンプレートとなる図形やグラデーション等)を格納する.プログラムにおけるサブルーチンを記述する場所に相当する.この要素内部の図形はレンダリング対象とはならず,use要素等により外部から参照されることで初めて意味をもつ.

そもそも描画対象とならない要素をわざわざdefs要素に入れる必要はないが,用途や意味合いを含めてdefs要素を使って整理しておくと,後々のコードのメンテナンスの際に役立つだろう。

use要素はxlink:href属性に設定した要素の内容を元に新しい図形を描画することを示す.位置,サイズについてはrect要素と同様に指定する.参照可能な要素には単一の図形要素の他,図形をグループ化するg要素,symbol要素がある.複雑なpathを使いまわすと言った用途に有効である.また応用としてハイパーリンクやスクリプトを仕込んだ図形をコピーすることも出来,工夫次第でhtmlでは不可能だったリンク情報の外部モジュール化等を実現することができる.

id
要素の識別子.htmlと同様に文書の中で一意となるように設定する.なおhtmlに埋め込んだsvg要素の場合は,html文書の中で一意となるようにする.一つのhtmlファイル内で(svg,htmlの両要素を跨いで)idが重複してはならない.
xlink:href
要素の参照先.スタンドアロンのsvg文書においてxlinkの名前空間を指定し忘れると,この属性が正しく動作しない.

参照する図形要素には特に制限はなく,defs要素外の要素も問題なくコピーできる.またg要素でグループ化されたものを指定可能だ.ただし,use要素自身の親要素を参照することはできない.コピー処理が循環してしまうため,このようなuse要素は描画処理において無視される.例を示す.

use要素の振る舞い

use要素は処理上g要素と同じように振る舞う.イメージとしては参照した図形要素をコピー(図形のインスタンスを生成)し,更にg要素で囲んだものがuse要素となる.従って,use要素に設定したスタイル属性値はコピー元の図形に値が設定されていない時のみ有効となる(g要素におけるスタイル属性の優先順位を参照).一方で,g要素と異なりuse属性でコピーした図形の一部を選ぶことはできない.(但しfirefoxに限りできてしまうが,これは明確な規定が無いためだと思われる.)

同じ図形をスタイルだけ変えて重ねると言った場合は,g要素と組み合わせると良い.図形の定義が一箇所にまとまるため,メンテナンス性が高い.

この方法を応用すると,通常fill→strokeの順で描画される順番を逆にすることができる.これは文字の縁どりを行う際に有効なテクニックだ.(なお, SVG2であれば同様のことをpaint-order属性だけで実現できる. 詳しくはmarkerの項を参照)

縁どり 縁どり

use要素の描画の基準

use要素に設定したx属性,y属性は図形をコピーする際の基準点となり,コピーされた図形はこの点を原点としてスクリーンに描画される.

use要素を用いた図形のモジュール化

同一のsvg(html)文書内の要素を参照するのみならず,外部のsvg文書を参照することも可能である.これを利用して図形のソースコードを外部モジュール化することが可能である.汎用的な図形を再利用する場合や,メインsvg文書とサブsvg文書に分解し,平行作業をする場合に有効である.なお,先ほど示した属性値の取扱いの問題から,汎用モジュールにおいては,図形の定義は座標指定に止め,fillやstroke等のプレゼンテーション属性は未設定のままの方が扱いやすい.

通常グラフィックツールで出力したsvgファイルには様々なプレゼンテーション属性が設定されてしまうため,テキストエディタでそれらを削除しておくと後から最利用する際に便利だ.「同じ形状」で「異なる色」の図形を描画する際にテンプレートとなる画像ファイルに一切手を入れること無く使う側で勝手にスタイルを設定できるようになるためだ.また次に示すsymbol要素においては図形の大きさまで制御出来るため,汎用性がより高い. 外部svg文書を参照したケースを示す.defs.svgには上記のdefsの内容を記述してある.

<?xml version="1.0" standalone="no"?>
<svg>
    <defs>
        <path id="star" d="M 69,107 C 62.262788,108.89248 53.757524,90.071163 47.194636,87.642174 40.631748,85.213184 21.92246,93.962125 18.040696,88.139463 14.158932,82.316801 29.430794,68.411708 29.712856,61.419432 29.994918,54.427155 15.892693,39.337137 20.230843,33.846058 c 4.33815,-5.491078 22.281944,4.736416 29.019156,2.84394 6.737212,-1.892475 16.730845,-19.96756 23.293733,-17.538571 6.562888,2.428989 2.380901,22.655021 6.262665,28.477683 3.881764,5.822663 24.160393,9.741664 23.878333,16.73394 -0.28206,6.992276 -20.810468,9.265158 -25.148618,14.756236 C 73.197962,84.610365 75.737212,105.10753 69,107 z"/>
    </defs>
</svg>

なお,当たり前のことであるが,クロスドメインでのsvgファイルの参照は無効である.また,ie9においてはuse要素による外部svgファイルの参照ができないので注意が必要となる.

また複数の図形をまとめてcssスプライト画像やアイコンフォント的な利用も考えられる.その際は煩雑となりがちな座標指定や文字コード指定をすること無く,意味を持ったidを指定することとなるので非常に判りやすい.唯一問題があるとすればコードの記述が長くなることくらい.

テンプレートファイルはこちら.

<svg class="icon"><use width="100%" height="100%" xlink:href="icons.svg#circle" fill="orange"/></svg>
<svg class="icon"><use width="100%" height="100%" xlink:href="icons.svg#circle-hollowed" fill="orange"/></svg>
<svg class="icon"><use width="100%" height="100%" xlink:href="icons.svg#rect" fill="orange"/></svg>
<svg class="icon"><use width="100%" height="100%" xlink:href="icons.svg#triangle" fill="orange"/></svg>
<svg class="icon"><use width="100%" height="100%" xlink:href="icons.svg#moon" fill="orange"/></svg>
<svg class="icon"><use width="100%" height="100%" xlink:href="icons.svg#pentagon" fill="orange"/></svg>
<svg class="icon"><use width="100%" height="100%" xlink:href="icons.svg#hexagon" fill="orange"/></svg>
<svg class="icon"><use width="100%" height="100%" xlink:href="icons.svg#star" fill="orange"/></svg>
<svg class="icon"><use width="100%" height="100%" xlink:href="icons.svg#star-nonfill" fill="orange"/></svg>

但し,use要素で参照している要素が更に別の要素を参照していた場合,その内容まで正しく引き継ぐかどうかはブラウザによって異なる.(仕様上は引き継ぐものとされている)

外部svg画像の読み込みとstyle要素

use要素は図形要素に設定されたid値を元に図形の複製を行うことから,外部svgファイルのルートとなるsvg要素にid値が設定されている場合に限りsvg画像の全体をインポートすることが可能となる.

しかし読み込んだsvg画像の全ての機能が有効となるわけではなく,script要素に依るスクリプト処理が無効となる.また参照先のsvg要素にstyle要素が含まれていた場合,意図しない挙動を示すケースがあるため,注意が必要である. 例を示す.style要素に依る塗り潰しを行なっているsvg画像をuse要素で参照している

<?xml version="1.0" standalone="no"?>
<svg width="200px" height="200px" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="main">
    <style>
        circle.hoge{
            fill:red;
        }
    </style>
    <circle class="hoge" cx="100" cy="100" r="80"/>
</svg>

するとfirefoxにおいては参照先のstyle要素の内容が呼び出し元のcss設定を汚染し,circle要素の塗り潰し色が書き換わってしまっている.chromeではstyle要素の動作そのものが行われず,塗り潰し色が黒のままである.

一般に図形モジュール化したsvgファイルにstyle要素が含まれていた場合,use要素の動作に影響を及ぼすため,スタイル付けをプレゼンテーション属性で行うか,style属性で行うべきである.またsvg画像全体を読み込むのであれば後述するimage要素を使う方法もある. このようにuse要素を画像のモジュール化に応用する場合はstyle要素に注意する必要がある.

use要素を用いた画像の分割

use要素とclipPath要素とを組み合わせることで単一の画像を複数のパーツに分割すると言った事が可能となる.図形の定義場所が一箇所にまとまるため,コードが簡潔になる.

なおuse要素はforeignObject要素とは相性が悪く,このテクニックを用いてhtml要素の複製を行おうとした場合はブラウザによって動作が異なるどころかhtmlの解釈が破壊されると言った問題を引き起こすケースがあるためお勧めしない.

symbol:図形のテンプレート

symbol要素を用いると,複数の図形を一括して一つの図形として扱うことが出来る.viewBox属性によって独自の座標系を定義し,その中でシンボル化したい図形を記述する.symbol要素はそのままでは何も表示されず,use要素を使って図形を複写することで画面に描画される.symbol要素はsvgの中でも使い勝手が良いため,共通図形モジュールとして定義しておくと便利だ.

symbol要素の特性

g要素が図形の論理的なひとまとまりを表すのに対し,symbol要素はviewBoxを定義し新たな図形を定義する点が異なる.g要素でグループ化した図形をuse要素で参照した場合と,symbol要素を参照したケースを比較してみよう.g要素の例ではwidth・height属性が無視されて,元の図形と同じものが指定の座標に表示されている.symbol要素の例では指定のサイズに拡大・縮小された上で図形が配置されている.

アスペクト比の取扱い

symbol要素とuse要素とでアスペクト比が異なる場合,preserveAspectRatio属性を使ってその図形の取扱いを制御することができる.詳しくは後述する.

スタイルの適用

symbol要素においても,元の図形にスタイル属性が設定されていない場合,use要素から属性値を設定することが出来る.但しstroke-widthをはじめとした値は,図形そのものがもつ座標を念頭に入れた値を設定する必要がある.つまり,図形の大きさの影響を受ける.

SVG2をサポートしている環境であればsymbol図形の定義にvector-effect="non-scaling-stroke"を指定する方法もある.この場合はstroke-width値をuse要素から指定することが可能となる.

symbolの多段参照

symbolによる図形の定義を用いて,予め基本図形を定義しておいて,それらを寄せ集めて別のシンボルとし,更に他の文書から参照すると言った多段の図形参照も可能だ.

下の例ではsubsub.svgで定義されたsymbolをsub.svgで組み合わせ,それをhtmlから読み出している.

subsub.svgの内容
<?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="c" viewBox="0 0 200 200">
        <circle cx="100" cy="100" r="100"/>
    </symbol>
    <symbol id="r" viewBox="0 0 200 200">
        <rect x="0" y="0" width="200" height="200"/>
    </symbol>
</svg>
sub.svgの内容
<?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="tile" viewBox="0 0 200 200">
        <use xlink:href="subsub.svg#c" x="0" y="0" width="100" height="100"/>
        <use xlink:href="subsub.svg#r" x="0" y="100" width="100" height="100"/>
        <use xlink:href="subsub.svg#r" x="100" y="0" width="100" height="100"/>
        <use xlink:href="subsub.svg#c" x="100" y="100" width="100" height="100"/>
    </symbol>
</svg>

このように図形ファイルの設計いかんによっては非常に再利用がしやすい構成となる.その一方作り方によっては,細切れのsvgファイルが多数できてしまうので,どのsvgファイルがどれを参照しているのかと言った管理が面倒となるため注意しよう.

図形の描画に関わる2つの座標系

一般にsvgにおいて図形を配置する際,図形そのものがもつ座標系(objectBoundingBox)とその図形を描画する先の座標系(userSpaceOnUse)の2つが考えられる.symbol内で図形を配置する際に用いているものはsymbolそのものがもつ座標系であり,use要素がsymbol要素を配置する際に用いているものはsvg要素のもつ座標系である.この座標系の取扱いによってx,y,width,height属性の持つ意味合いが変わってくるのだ.今後この座標系の切り替えがしばしば出てくるのでよく意識しよう.

描画オブジェクトがもつ座標系 svg要素が持つ座標系

overflow属性

viewBox属性を持つ要素(svg要素,symbol要素,marker要素)にはoverflow属性が定義されている.viewBoxの範囲外に図形が描画された際,visibleではそのまま描画され,hiddenでは範囲外の部分が描画されないようになる.なお,設定値はhtmlのoverflow属性からそのまま流用していて,その意味合いをsvgにて再定義しているので若干違和感を覚えるが気にしない.たとえ値scrollが設定されていたとしても,svgにおいてはスクロールバーが表示されることはない

ie9においてはインラインsvgでの描画内容が外部にはみ出すと言った現象が発生するが,これはoverflow属性をhiddenに設定することで解消する.

overflow
viewBoxからはみ出した部分をどう表示するか.
visible
表示する
hidden
表示しない
scroll
hiddenと同じ.
auto
visibleと同じ.
inherit
親要素から継承する.

これはつまりsvgにおいてはスクロール機能が存在しない事を示している.svgのレイアウトは基本的に固定的であり,htmlにおけるボックスモデルのような構造がsvgには存在しない.従ってスクロールが必要となるような動的なレイアウトが必要な場合はhtmlと組み合わせるのが常套手段となる.

補足)グラフィックの存在しないsvgファイル

svgファイルをビューアで開いた場合,時折何も表示されないものが存在することに気がつくだろう.一見空のファイルと勘違いしがちだが,ラスタ形式の画像と異なりsvgでは必要不可欠なファイルの場合がある.

ここで示したdefs要素など,svgではグラフィック部品をモジュール化する仕組みが定められているため,他のsvgファイルから参照されることを前提としたファイルも存在している.このようなファイルをビューアで開いても,直接グラフィックを描画する為の要素が定義されていないので,見た目真っ白となってしまうのだ.従って,そのsvgファイルが本当に何も意味が無いものかどうかは,テキストエディタを使ってその中身を確認する必要がある

モジュール化される可能性のある要素:defs要素,symbol要素,linearGradient要素,radialGradient要素,pattern要素,clipPath要素,mask要素,filter要素,font要素

svgに不慣れなメンバはどうしてもsvg=グラフィックの先入観を持ってしまいがちであり,このようなsvgモジュールをビューアの表示結果のみを頼りに中身が無いものと思い込むことは十分にありうる.その結果,意図しないファイルの削除やドローイングツールによるファイルの破壊を引き起こすことも考えられる.よってこのようなヒューマンエラーを減らすためにsvgモジュールに次のような細工を施しておくことはシステムの防衛上有意義なことである.

つまり,ソースコード上のコメントだけでなくビューアからもそのファイルの役割を確認出来るようにしておくことが肝心なのだ.

image:画像の外部参照とアスペクト比

htmlのimg要素と同様にimage要素を用いて外部のpng,jpg,svg等の画像ファイルを取り込むことが出来る.外部ファイルの参照の他,dataスキームによる画像データの埋め込みに対応しているなど役割的に非常に似通った要素であるが,細かい部分で動作が異なる.

image要素においては画像を表示するための領域を定めるといった意味合いが濃く,img要素のもつ画像の埋め込みといったニュアンスとは若干異なるのだ.

アスペクト比の設定

image要素においてはpreserveAspectRatio属性を用いて,画像の引き伸ばし方法を指定することが出来る.この機能は使い方次第でグラフィックの左詰め・右詰めと言った処理に応用できる.

xlink:href
画像の参照先uri.htmlのimg要素ではsrc属性であったため,名称が異なる点に注意する.
preserveAspectRatio
画像の引き伸ばし方法の指定.image要素,feImage要素(画像を扱う要素),svg要素,symbol要素,pattern要素,marker要素,view(viewBox属性を持つ)要素で有効.なお,chromeにおいてはimg要素や,image要素からsvgグラフィックを参照した場合に設定が無視される.
xMinYMin等の位置を指定する設定値はmeet,sliceの前に記述する
defer
参照先しているsvg画像のpreserveAspectRatio設定を優先する.
xMinYMin
画像の位置指定※例を参照
xMinYMid
画像の位置指定※例を参照
xMinYMax
画像の位置指定※例を参照
xMidYMin
画像の位置指定※例を参照
xMidYMid
画像の位置指定※例を参照
xMidYMax
画像の位置指定※例を参照
xMaxYMin
画像の位置指定※例を参照
xMaxYMid
画像の位置指定※例を参照
xMaxYMax
画像の位置指定※例を参照
none
範囲いっぱいに引き伸ばす
meet
比率を保ちつつ,画像を内部にフィットさせる.image要素の領域に隙間が開く.規定値
slice
比率を保ちつつ,領域いっぱいに広げる.画像が見切れる.

引き伸ばしの方法

画像及びimage要素には次に示すような9つのような基準点が定められており,preserveAspectRatio属性に指定すると画像の基準点とimage要素の基準点とを重ね合わせるように画像が配置される.

xMinYMin xMinYMid xMinYMax xMidYMin xMidYMid xMidYMax xMaxYMin xMaxYMid xMaxYMax image要素

その上で,meet,sliceを指定して画像の引き伸ばし方法を定める.なお,noneの場合は画像のアスペクト比が変更され,image要素とぴったりと重なるように引き伸ばされるため先ほどの位置の指定は必要ない.

none meet slice

なお,width,height,veiwBox属性が定義されていないsvgファイルを参照した場合,正しく画像の引き伸ばしが行われない.ツールを使ってsvgファイルを出力した際にこのようなケースが発生するが,上手くsvg画像の引き伸ばしができない場合はこれらの属性が定義されているか確認しよう.

preserveAspectRatio属性を適用可能な要素

preserveAspectRatio属性はimage要素の他にもsvg要素等のviewBox属性をもつ要素に設定可能で,画像の表示サイズとviewBoxの大きさとの間にアスペクト比の差異があった際の画像の引き伸ばし方法を表す.

画像の回転とアスペクト比

image要素の画像をtransform属性を使って回転するとpreserveAspectRatio属性による画像のリサイズ処理が判りにくくなる.画像を回転させつつ正しくリサイズ処理を行うには,svg要素で画像を囲み新たなビューポートを定義すると良い.

svg画像の取扱い

image要素でsvgファイルを表示した場合は,htmlのimg要素と同じくjavascriptが動作しない.

画像の右クリックを無効化する

インターネット上で画像を公開する際,なるべく画像をローカルに保存させたくない場合がある.svgの機能を用いることで画像の保存操作をやりにくくすることが出来る.

htmlにおける画像は通常img要素で読み込むが,この要素にカーソルを当てて右クリックすることで画像を保存するためのメニューが表示される.このメニューはjavascriptを用いることで無効化することが可能なものの,スクリプトが無効化されている環境では意味がない.

そこでsvgのimage要素とuse要素を用いて画像を読み込むことを考える.

svg要素を二つ用意し,片方を非表示としながら中でimage要素を使って画像を読み込み,もう片方ではそのimage要素をuse要素を使ってスクリーンに描画するのだ.こうすると,スクリーンに見える画像の実体はuse要素であるため,右クリック時のコンテキストメニューに画像の保存に関わるものが表示されないのだ.ie9においてはsvg要素そのものをsvgファイルとして保存できてしまうが,保存したsvg要素の中身には実際の画像へのurlが存在しないので問題はない.

ソースコードを見ればどの画像を読み込んでいるかは明らかだが,画像の保存にはそれなりのスキルが必要となるため,これだけでもかなりの効果が期待できる.

svgを用いた回転画像の生成

htmlにおいてラスタ画像を挿入する場合,画像を回転させた上で表示させたい場合がある.その際,css3のtransformプロパティを使う他にsvgを間に挟んで対処する方法がある.この方法ではsvg内部で画像を回転させるため元のラスタ画像が維持される上,html側では画像の回転を意識する必要がないといったメリットが得られる.

<?xml version="1.0" standalone="no"?>
<svg width="200px" height="200px"
    xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"
    viewBox="0 0 200 200" preserveAspectRatio="none">
    <image xlink:href="img.png" width="200" height="200" transform="translate(200,0),rotate(90)"/>
</svg>

但しsvgの動作モードの都合上img要素からsvgを読み込んだ場合,回転対象のラスタ画像を表示できないケースがあるため,object要素を用いる必要がある.(左がimg要素,右がobject要素)