htmlと同様にsvgにおいてもjavascriptを用いてその内容を操作することができる.svgを描画するjavascriptライブラリとしてはRaphaël—JavaScript Libraryが有名だが,本項ではより応用の効く,直接svgのもつapiを使ってグラフィックを描く方法について示す.因みにRaphaëlの使い方は別の項で述べる.なお,htmlでのjavascriptの利用法を理解していることを前提としているので,内容が解りにくかった場合は別途他のサイトを参照して欲しい.
htmlと同様にsvgにおいてもjavascriptを用いてその内容を操作することができる.svgを描画するjavascriptライブラリとしてはRaphaël—JavaScript Libraryが有名だが,本項ではより応用の効く,直接svgのもつapiを使ってグラフィックを描く方法について示す.因みにRaphaëlの使い方は別の項で述べる.なお,htmlでのjavascriptの利用法を理解していることを前提としているので,内容が解りにくかった場合は別途他のサイトを参照して欲しい.
script要素はsvgファイル内部にjavascript(厳密にはapplication/ecmascript)によるプログラム処理を記述するためのもので,htmlにおけるscript要素とほとんど同じ機能をもつ.htmlファイルにおいて埋め込みsvg要素を用いた場合,style要素と同様にhtmlの要素とsvgの要素とを混在させたスクリプトも正しく動作する.
html等の文書においては,dom(Document Object Model)と呼ばれるインターフェースが定義されており,スクリプトなどはこのdomを介して文書を操作する.domは文書の種類により様々なものがあるが,svgにおいてはsvgdomが,htmlにおいてはhtmldomが定められている.どちらも汎用的なxml操作の為に策定された「dom core」を拡張したものとなっており,共通的な要素の操作(例:element.appendChild, element.removeChild, document.getElementById, document.getElementsByTagName等)はここで定義されている.従って,svgdomでもhtmldomでも基本的な使い方に違いはない.以下に関係を示す.
htmldomとsvgdomとは兄弟の関係にあるものの,文書の特性に合わせて独自のapiを持つため,直接的な関連を持たない.従ってhtmldomを前提に設計されたライブラリ(例えばjQuery)はsvg文書においては動作しない.
html文書をブラウザで開いた場合,変数documentにHTMLDocumentオブジェクトが,svg文書を開いた場合はSVGDocumentオブジェクトが設定される. また埋め込みsvg要素を使ったhtml文書の場合は,document.getElementByIdメソッドで得られるオブジェクトがSVGElementの可能性があり,htmldomを前提としたプログラムを作成するとエラーとなる.
上記以外についてはほとんど同じである.例えばjavascriptのもつ関数(alert等)は全て利用可能であるし,window.location等のオブジェクトもdocumentプロパティの実体が異なる点を除き同じである.またスクリプトの実行タイミングもscript要素が読み込まれた時点であるため,存在しない要素にアクセスしようとするとエラーとなる.このように,基本的な挙動についてはhtmlのものと同じであるため,既存のノウハウをそのまま流用することができる.
但しsvgファイルは文書として扱う場合の他に画像ファイルとして扱う場合があるが,この時にjavascriptが実行されるとは限らない.svgファイルをiframe要素に設定した場合はjavascriptが動作するが,img要素のsrcに設定した場合やbackground-imageに設定した場合はjavascriptが無効となるのだ.従って,動きのある背景を作りたいといった場合には,animate要素を用いるなど静的な構造で動きを表現することとなる.なお,埋め込みsvg要素についてはjavascriptは問題なく動作する.(もちろんブラウザ側でjavascriptが実行可能となっていなければならない.)
svg文書をメイン文書とサブ文書に分解する場合,メイン文書でのみ正しくスクリプトが動作する点に注意する.use要素も,image要素も参照先のsvg文書のスクリプトまでは実行してくれる保証はない.svgだけではhtmlでのframeやiframeのような複数の文書が連携すると言った機構を取りにくいのだ.よってスクリプトはメイン文書に集約し,サブ文書はグラフィックに特化させたり,htmlでsvgを利用することを前提とした設計をすると良い.
一般にsvgにおいてスクリプトプログラムを挿入するにはhtmlと同様の機構を含め次に挙げる2〜3つの方法がある.
最も一般的な記述法であり,svg文書が先頭行から順に読み込まれ,script要素が読み込まれたタイミングで直ぐに実行されるという特徴を持つ.スクリプトが記述された外部ファイルを参照することもできる.
script要素には原則的にjavascriptによるプログラムを挿入することになるが,環境によっては別の言語を指定することもできる.例えばIE環境では「text/vbs」を指定することでvbs(visual basic scripting edition)を使ってスクリプトを記述することが可能だ.※本文書ではvbsについては触れないので必要に応じて各自調べて欲しい.
※正直な所スクリプト言語にvbsを採用するのはお勧めできない.あくまでも下位互換性を保つ目的で用意されているものとして考えよう.但しvbs自体は優れた言語なので,使い所さえ間違えなければ非常に便利である.
svgを構成する要素にはそれぞれ「on」から始まるイベント属性と呼ばれる属性が定義されており,ここにスクリプトを記述することができる.例えば「onclick」属性にスクリプト「alert("hellow")」を記述しておけば,この要素をマウスなどでクリックした際にスクリプトが実行され,メッセージ「hellow」が表示されるだろう.
svgで用いることが可能なイベント属性については本ページの後半にて示したのでそちらを参照して欲しい.なお,全ての要素で全てのイベント要素が有効となるとは限らない点に注意すること.
script要素と同様にイベント属性に記述するスクリプトの言語はsvg要素のcontentScriptTypeにて指定することができる.
svg1.2 tinyではイベント属性をサポートしないため,イベント処理を実行するための要素が定義されいる.なおsvg1.1では利用できないので,気にする必要はない.
htmlをベースとした(svg要素をhtmlに埋め込んだ際の)スクリプティングとsvgをベースとした(svgファイル単体で動作する)それとでは自ずと異なる点が出てくるのでまとめてみた.
html | svg | 備考 | |
---|---|---|---|
scriptファイルの外部参照 | <script type="text/javascript" src="参照先"></script> | <script xlink:href="参照先"/> | 記述がxlink形式となる.svgではcharset属性が定義されていないため,スクリプトは文字コードに原則としてutf-8を選ぶと良いかも知れない. |
※スタイルの外部参照 | <link rel="stylesheet" type="text/css" href="参照先"/> | <?xml-stylesheet href="参照先" type="text/css"?> | 記述がxml形式となる. |
<![CDATA[]]>の必要性 | 必要なし.xhtmlでは必要 | スクリプトをファイルに埋め込む場合は必要. | script,style要素の中身を囲む記述.さもないと,「>」等の演算子を「>」とエスケープしなければならず,コードの可読性を損ねる. スクリプトを参照する場合はこの限りではない. |
window.documentの実体 | HTMLDocumentオブジェクト | SVGDocumentオブジェクト | ほとんど同じAPIを持つが,細かい部分で異なる.特に要素の生成に注意のこと. |
文書の描画を行うルート要素 | document.body | document.documentElement | SVGSVGElementが返される. |
htmlと異なり,svgがxml文書であることからこのような違いが現れる.htmlとsvg間でソースコードを移動させる場合はこれらの点に注意すること.
svg文書が読み込まれた際にスクリプトを実行するにはウインドウのloadイベント(window.onload)svg要素のloadイベント(document.documentElement.onload)の何れかに関数を設定すればよい.ここを起点にsvg画像を構成する各要素へアクセスすることとなる.
svgの要素(SVGElement)を取得するには,これまでどおりdocument.getElementByIdやdocument.getElementsByTagNameメソッドを利用することができる.
一方SVGElementの生成にはdocument.createElementNS("http://www.w3.org/2000/svg", "要素名")を用いる.これはsvgに関わる要素であることを明確化するためのもので,document.createElementメソッドではhtml要素が指定されたと解釈される.(なお,テキストノードの生成にはdocument.createTextNodeメソッドを用いる.)
つまりdocument.createElement("circle")と指定すると,svgのcircle要素ではなく不明なcircle要素が生成される.無論この不明な要素はレンダリング時に無視されるため,このことに気がつかないで実装を進めてしまうと,非常に根深いバグとなる可能性がある.デバッガのdomツリー上でも見た目上問題ないからだ.以下に例を示す.
ただ一箇所異なるだけで,前者では何も表示されていないことがわかる.
svgの名前空間uriはsvg要素等のnamespaceURIプロパティから内容を取得することができるため,既にsvg要素がdomに存在している場合は次のように記述することができる.
もちろんインラインsvgを自動生成する場合など,大本となるsvg要素が存在しない場合は直接名前空間uri文字列を指定する必要がある.
SVGElementの属性値の参照・設定をするには主に次の4つの方法がある.
†この形式をcamel形式と呼ぶ.
複雑な記述を要する属性値については専用のアクセスメソッドが提供されている場合がある.属性値の文字列をスクリプトで構築するのは,浮動小数点型の数値を連結する際に丸めなどの処理が必要となりいささか面倒だが,専用メソッドを用いればそのような事を考えずに済む.一方で記述量が多くなりがちなので適宜使い分けると良いだろう.なお,メソッドの一覧についてはmozillaのsvgのAPI解説ページ/MSDNのsvg解説ページに詳しい.以下に主なものを示す.
d属性は直線や円弧等の操作毎にオブジェクトが提供されており,pathSegListプロパティにリストとして管理されている.このオブジェクトを操作することでパス切片毎に制御することが可能だ.以下は先程の例をsvgDOMの内容を用いて書き換えたものである.
points属性はSVGPointオブジェクトのリストとして実装されている.
text要素,tspan要素,tref要素,altglyph要素はSVGTextPositioningElementインターフェースを実装しており,x,y,dx,dy,rotate属性に相当するプロパティはSVGAnimatedLengthList/SVGAnimatedNumberListとして実装されている.従って単一の値を設定するにしても直接値を挿入することが出来ず,一旦SVGLengthオブジェクト等を生成してからリストに値を追加することとなる.
数有る属性の中でも最も扱いが面倒なものがこのtransform属性だ.以下にその構成を示す.
circle,rect等の図形要素はSVGTransformableインターフェースを実装しており,trasformプロパティをSVGTransformListオブジェクトとして実装している.同様にlinearGradient,radialGradient要素はgradientTransformプロパティを,pattern要素はpatternTransformプロパティをSVGTransformListとして実装している.他のプロパティと同様にこれらのプロパティもbaseVal,animValを持っているので適切に選択する必要がある.
上で得られるSVGTransformListオブジェクトには,SVGTransformオブジェクトのリストが格納されている.このSVGTransformオブジェクトはscale,skewX,skewY,rotate,translate,matrix関数に相当するもので,それぞれ対応するメソッドが定義されている.関数の設定を取得するためのプロパティにはtype(関数の種類),angle(角度),matrix(設定値)の3つがあり,直接transform属性に設定した文字列を取得する方法は無い.
この3つのうち,matrixプロパティから得られるSVGMatrixオブジェクトはtransformの項で示したアフィン行列に相当するものだ.a〜fのプロパティから構成されており,これらの値を変更すると即座にグラフィックの内容が書き換わる.
例えば,e,fプロパティを書き換えることで平行移動を表現することが出来る.
これらのリスト状のオブジェクトに共通して次のインターフェースが実装されている.一般的なインデクサとlengthプロパティによる配列的なアクセスはできず,代わりにgetItemメソッドとnumberOfItemsプロパティを用いる.なお,firefox,operaにおいては配列と同様のインデクサが提供されているが,これはchromeで動作しないので注意のこと.
path要素のd属性についてはsetAttribute/getAttributeメソッドで中身を操作する他に,pathSegListプロパティ(SVGPathSegListオブジェクト)を介してパス切片(SVGPathSegオブジェクト)毎に編集できる.ここでパス切片とはパスを引く際のパス操作を指す.
path要素における操作にはMやLを始めとした合計19種類が存在するが,domではこのそれぞれに固有のオブジェクトが割り当てられている.以下にその一覧を示す.
操作 | オブジェクト | 内容 | 種類 | type |
---|---|---|---|---|
- | - | 不明 | - | 0 |
z/Z | SVGPathSegClosePath | パスを閉じる | - | 1 |
M | SVGPathSegMovetoAbs | パスを開始する | 絶対 | 2 |
m | SVGPathSegMovetoRel | 相対 | 3 | |
L | SVGPathSegLinetoAbs | 直線を引く | 絶対 | 4 |
l | SVGPathSegLinetoRel | 相対 | 5 | |
C | SVGPathSegCurvetoCubicAbs | 3次スプライン曲線を引く | 絶対 | 6 |
c | SVGPathSegCurvetoCubicRel | 相対 | 7 | |
Q | SVGPathSegCurvetoQuadraticAbs | 2次スプライン曲線を引く | 絶対 | 8 |
q | SVGPathSegCurvetoQuadraticRel | 相対 | 9 | |
A | SVGPathSegArcAbs | 円弧を引く | 絶対 | 10 |
a | SVGPathSegArcRel | 相対 | 11 | |
H | SVGPathSegLinetoHorizontalAbs | 水平線を引く | 絶対 | 12 |
h | SVGPathSegLinetoHorizontalRel | 相対 | 13 | |
V | SVGPathSegLinetoVerticalAbs | 垂直線を引く | 絶対 | 14 |
v | SVGPathSegLinetoVerticalRel | 相対 | 15 | |
S | SVGPathSegCurvetoCubicSmoothAbs | 3次スプライン曲線を引く(第一制御点省略) | 絶対 | 16 |
s | SVGPathSegCurvetoCubicSmoothRel | 相対 | 17 | |
T | SVGPathSegCurvetoQuadraticSmoothAbs | 2次スプライン曲線を引く(制御点省略) | 絶対 | 18 |
t | SVGPathSegCurvetoQuadraticSmoothRel | 相対 | 19 |
リストの中身を操作するためにはこれらのオブジェクトを識別する必要があるが,ご覧の通り名称が余りにも冗長なため,instanceof演算子による判定はいささか面倒である.そのためか,svgdomではこの他にpathSegTypeプロパティやpathSegTypeAsLetterプロパティが定義されている.前者はパス操作のタイプを返すもので,上の表におけるtype列の値を返す.後者はパス操作文字そのものを返す.type値を2で割るときれいに絶対位置指定,相対位置指定を分類できるので場合により使い分けるとよい.
また,パス切片オブジェクトのプロパティを操作する場合,いちいち型チェックをせずともそのオブジェクトがプロパティを持っているか(undefinedでない)を確認するだけで良い.
プロパティ種類 | 説明 | 型 | M/m | L/l | C/c | Q/q | A/a | H/h | V/v | S/s | T/t |
---|---|---|---|---|---|---|---|---|---|---|---|
x | 終点のx座標 | float | ○ | ○ | ○ | ○ | ○ | ○ | ○ | ||
y | 終点のy座標 | float | ○ | ○ | ○ | ○ | ○ | ○ | ○ | ||
x1 | 制御点1のx座標 | float | ○ | ○ | |||||||
y1 | 制御点1のy座標 | float | ○ | ○ | |||||||
x2 | 制御点2のx座標 | float | ○ | ○ | |||||||
y2 | 制御点2のy座標 | float | ○ | ○ | |||||||
r1 | x座標方向の半径 | float | ○ | ||||||||
r2 | y座標方向の半径 | float | ○ | ||||||||
angle | 角度 | float | ○ | ||||||||
largeArcFlag | 円弧の選択 | boolean | ○ | ||||||||
sweepFlag | 円弧の方向 | boolean | ○ |
※先頭のm操作はM操作と同等である.絶対位置指定・相対位置指定で処理を分ける必要がある場合は先頭のパス切片のみ例外処理としよう.
※A,a操作についてはフラグ値や角度が存在しているため,変形処理を行いにくい.事前にベジェ曲線に置き換えるなどしておくと良い.
このようにd属性を編集するのは面倒な場合が多いため,(path要素からglyph要素を生成するなど)特に必要がない限りpath要素を変形する場合はtransform属性を編集したほうが良い.
手入力やスクリプトでパス操作を出力した場合,d属性の内容が正しい書式となっているとは限らない.ここでsvgの仕様においては,パス操作は問題のある箇所から後ろの部分を無視し,正しい部分までを描画することとなっており,pathSegListプロパティには正しい範囲のpathSegオブジェクトが格納されている.従って,pathSegListの内容からパス操作文字列を再構築することで,問題のあるd属性文字列から正しい部分のみを取り出すことができる.
子要素の全体を取得するプロパティとしては,childNodesを用いる.この場合無用なテキストノードが含まれてしまうが,これはノードのnodeTypeプロパティを確認することで要素ノード(1)かテキストノード(3)か判別できる.
なお,要素ノードのみを抽出可能なchildrenプロパティが存在する場合があるが,これは元来HTMLElementで利用可能なものであり,場合により不具合を引き起こす.※当初childrenを用いるとテキストノードが取り除かれて便利だとしていたが,これは間違いだと考えられる.実際chromeでエラーが発生してしまう.
その他,firstChildやlastChildのと言ったものに加え,firstElementChild,lastElementChildといったプロパティを用いることもできる.これらのプロパティでは自動的にnodeTypeが1の要素ノードのみが抽出されるので使い勝手が良い.(これらはElement Traversal Specificationで定義されている.)
子要素の追加・削除はappendChild,removeChildメソッド等これまでと同様に行える.
style要素,script要素,text要素の内部のテキストにアクセスするにはtextContentを用いる.文字列のエスケープ(><&等)を自動的に行うため,扱いやすい.その一方であくまでテキスト部分にアクセスするものであるため,innerHTMLのような要素記述を挿入するといった用途には使えない.
svg要素の内容は次の何れかを利用することでsvgソースコードとして取得することが出来る.この仕組みを利用するとスクリプトで生成したsvgグラフィックを外部に取り出すことが出来るため,webブラウザをsvgエディタとして利用することも可能となる.
XMLSerializerオブジェクトのserializeToStringメソッドを用いることで簡単にdomの構成をテキストに変換することが出来る.
svg要素を囲んでいるhtml要素のinnerHTMLプロパティを参照することで間接的にコードの参照・書き換えは可能である.※本サイトではこのテクニックによりsvg要素部分のソースコードを直接ページ上に表示している.
また,ブラウザによってはxlink:hrefの属性名がhrefに切り詰められてしまうので,適宜書きなおす必要がある.
この機能で得られたsvgコードには名前空間の指定が欠落している場合がある.従って,スタンドアロンのsvgコードとして扱いたい場合は,予めsvgとxlinkの名前空間設定を明示すると良い.
svgはxmlをベースとしていることから,webブラウザのもつajax機構を利用することで外部のsvg画像を非同期に読み込むことも可能である.以下にXMLHttpRequestオブジェクトを使った外部svg読み込みのサンプルを示す.
<?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">
<script><![CDATA[
window.onload = function(){
var req = new XMLHttpRequest();
req.open("GET", "sample.svg", true);
req.onreadystatechange = function(e){
if(req.readyState != 4){return;}
if(req.status == 200 || req.status == 0){
var svg = req.responseXML.documentElement;
svg.width.baseVal.value = 200;
svg.height.baseVal.value = 200;
document.documentElement.appendChild(svg);
}
};
req.send(null);
};
]]></script>
</svg>
スタンドアロンのsvgを利用している場合,svgのソースコードをdomオブジェクトに変換するにはDomParserオブジェクトを用いる.これはsvgにhtmlのinnerHTMLプロパティのような仕組みを持たないためである.この場合,正しくsvgdomとして解釈されるためにはソース内部に名前空間設定が含まれている必要がある.
<script>
var source = "<svg width='200px' height='200px' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1'><circle cx='50' cy='50' r='40' fill='orange'/></svg>";
var parser = new DOMParser();
var svgDoc = parser.parseFromString(source, "text/xml");
var svg = document.querySelector("#svgCanvas");
svg.appendChild(svgDoc.documentElement);
</script>
親となるhtmlファイルと同じドメインのhtml/svg文書をiframe・object要素で参照している場合は,親文書からフレーム内部の構造を操作することができる.逆にimg要素等から参照したsvg画像をスクリプトから操作することは出来ない.(※ajax等で別途svgを読み込み,内容を編集してからdataスキーム形式にすると言ったやり方は考えられる.)
window.addEventListener("load", function(){
var objElem = document.getElementById("objElem");
//フレーム内部のsvg要素を取得する.
var svg = objElem.contentDocument.documentElement;
var circle = document.createElementNS("http://www.w3.org/2000/svg","circle");
circle.cx.baseVal.value = 100;
circle.cy.baseVal.value = 100;
circle.r.baseVal.value = 80;
circle.style.setProperty("fill", "green");
circle.style.setProperty("fill-opacity", "0.5");
svg.appendChild(circle);
},false);
複雑な図形を定義した場合に,その図形を囲む矩形範囲が判ると何かと都合が良い場合がある.このような場合,getBBoxメソッドを実行することで図形の位置及びサイズをSVGRectオブジェクトとして取得することができる.この値にはstroke幅等が含まれず,純粋に図形のサイズとして算出される.
loadイベント直後など,svgの描画のタイミングによっては正しく値を取ることが出来ないようなので,適宜try-catch構文等でエラー制御を行うと良い.
animate要素でアニメーションしている図形についてもサイズを取得できるが,あくまでも図形要素が描画されている座標系における描画範囲なので,transform属性が設定されていた場合には得られる値と見た目の座標との間に乖離が発生する.
svg tiny 1.2のdomapi(svg micro dom)のメソッドではあるが,operaにおいてはgetScreenBBoxメソッドを用いることでtransform属性が設定されていた場合の見た目の表示範囲を取得可能だ.しかし,必ずしも正しい値とはならないようなので注意のこと.
text(及びtspan/textPath)要素によって描画されている文字の位置を取得するには専用のSVGTextContentElementインターフェースを利用すると良い.先ほどのgetBBoxメソッドがtext要素全体を対象とするのに対し,部分文字列に対する座標を求めることが出来る.以下にメソッドの相関について示す.
文字が傾いていた場合,getExtentOfCharによって得られた矩形が実際のサイズよりも大きくなる点に注意しよう.例を示す.
この例では傾きを考慮した上で文字を矩形で囲んでいるが,この矩形は簡単な三角関数による計算で求められる.
webブラウザが捕捉するマウスイベント等の発生位置は,通常スクリーンやブラウザの描画位置を基準に行われるが,その際に左上を原点とする座標系(スクリーン座標)を考えることが出来る.一方svg図形はsvg要素の描画位置とサイズsvg要素のviewBox属性,preserveAspectRatio属性図形要素に対するtransform属性の3つから定まる座標系を基準に描画される.するとこの二つの座標系の間に変換行列(ScreenCTM)を考えることができる.
ここでスクリーン座標(X,Y)とsvg要素内部の座標(x,y)との間には次の関係が成り立つ.
通常カーソルイベントによって得られる座標値はスクリーン座標(X,Y)から求められるので,上記の式に左から[ScreenCTM]の逆行列を掛けることで次の式が成り立つ.
この式に基づくと,下記の手順に従いスクリーン上のカーソル位置にsvg画像を描画する事が可能となる.
use要素はノードツリーを複製して図形を描画するが,SVGUseElementオブジェクトの持つinstanceRootプロパティから現在参照しているノードを操作することが出来る.なお,複製した図形要素にアクセスする方法は無い.現在operaでのみ動作する.
また,イベント処理を行う場合にuse要素が関連する場合は次のような注意点が存在する.通常イベントを発生したオブジェクトはイベントオブジェクトのtargetプロパティを参照することで取得できるが,この内容がブラウザによって異なるのだ.下記の例のようにuse要素で描画した内容をクリックすると,firefoxではSVGUseElementが,chromeとoperaにおいてはSVGElementInstanceオブジェクトが取得されてしまう.なお後者の場合はSVGElementInstanceオブジェクトのもつcorrespondingUseElementからその図形を生成したsvg要素への参照を得ることができるので,さほど問題とはならないはずだ.
図形要素のonclick等のイベント属性にスクリプトを設定しておくと,use要素を使ってそのスクリプトごと要素をコピーすることができる.その際のスクリプトはコピーした文書内に参照先の要素がそのまま存在しているかのように動作する.この動作は外部svgを参照している場合も同様であり,イベントに関わる処理については外部モジュール化することが可能となっている.但しoperaでは動作しないようだ.
<?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">
<defs>
<g id="clickable" onclick='
dispMsg(this);
'>
<circle cx="100" cy="100" r="80"/>
<text x="100" y="110" text-anchor="middle" fill="white">click me!</text>
</g>
</defs>
</svg>
但し,このテクニックを用いた場合に注意すべき点としては,先程示した通りイベントを発生したオブジェクトがchrome/operaではSVGElementInstanceであるのに対し,firefoxでは図形要素そのものが取得されてしまう点がある.これは明らかにfirefoxのバグ(w3cに準拠していない)と言える.
svgで頻出するhref属性はもともとxlinkにおいて定義されているプロパティなので,setAttributeメソッドを用いた場合やsetAttributeNSメソッドにおいて名前空間の設定を間違えると正しく動作しない.正しくxlinkに相当する"http://www.w3.org/1999/xlink"を指定するか,hrefプロパティを直接操作すること.これはxlinkを名前空間に持つ他の属性についても同様である.このようにsetAttributeNSメソッドによる値の設定は非常に間違いに気づきにくいと言ったデメリットが存在するため,できる事ならsvgdomが提供しているapiを利用するようにしたい.
use.setAttribute("href", "icons.svg#star");
use.setAttributeNS("http://www.w3.org/2000/svg", "href", "icons.svg#star");
use.setAttributeNS("http://www.w3.org/1999/xlink", "href", "icons.svg#star");
use.href.baseVal = "icons.svg#star";
image要素に対応するSVGImageElementは,htmlにおけるHTMLImageElementのcompleteプロパティに相当する機能を持たない.従って画像読み込みの完了処理はloadイベントを用いる必要がある.(が,chromeではこのloadイベントが発生しないようだ.)
同様に画像の読み込みに失敗したことを取得する場合はerrorイベントを用いると良い.ブラウザによっては読み込み失敗のアイコンが表示されてしまうので,visibility値をhiddenに設定し画像を隠してしまおう.
なおloadイベントは仕様ではsvg要素・image要素の他に設定することができるとあるものの,実際に試してみると動作しないようだ.
chromeにおいてimage要素のloadイベントが発生しない問題は代替としてhtmlのimg要素を用いることで回避することが出来る.foreignObject要素を宣言し,その中にimg要素を配置しておく.後は先ほどと同様に記述することで画像読み込み時・読み込み失敗時の処理を実行することが可能となる.
文書に追加した要素はDOMツリー上に存在し続けるため,余りに多量の要素を追加した場合パフォーマンスに影響を及ぼす.この場合は,描画のタイミングを調整したり,svg要素で表現していた図形を事前に外部ラスタ形式の画像ファイル(pngやjpg)とできないか検討する.
もしくは描画後の動作が軽快なcanvas要素を利用する手もある.svg要素を全てcanvas要素で置き換えてもよいが,canvasのもつtoDataURLを使って描画処理の一部を置き換えることが出来る.toDataURLで得たdataスキーム形式の文字列をsvgのImage要素に設定して,svgに画像を埋め込んでしまうのだ.こうすると,svgの持つ「構造が保たれる,図形の移動・変形が容易」といったメリットを残しつつ,スクリプトの軽量化を図ることができる.
それとは逆にsvg画像をimg要素に読み込ませれば,canvas要素に書きこむことができる.だが,構造が複雑な場合エラーとなるケースがある.例えばcanvasで作った画像をsvgでfilterを掛けて再度canvasに書き戻すと言った処理は(クライアントサイドスクリプト単体では)実現が難しい.
なお,canvas要素にsvg画像を描画するライブラリcanvgを利用する方法もある.
htmlではlink要素を操作することでも簡単にスタイルシートを切り替えることができるが,スタンドアロンのsvgにおいては概ね次の3つの方法が挙げられる.しかし実際に試してみると実用に耐える方法は非常に限られている.
svg文書に適用されうるスタイルシートを予め代替スタイルシートとして定義しておき,それをスクリプトから操作する方法である.documentオブジェクトのselectedStyleSheetSetプロパティにtitleで定義したしたスタイルの名称を指定する.
<?xml version="1.0" standalone="no"?>
<?xml-stylesheet href="cssa.css" type="text/css" title="main"?>
<?xml-stylesheet href="cssb.css" type="text/css" title="sub" alternate="yes"?>
<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">
<rect x="20" y="20" width="160" height="160"/>
<script>
window.onload = function(){
//selectedStyleSheetSetメソッドを使って代替スタイルに変更する.
document.selectedStyleSheetSet="sub";
};
</script>
</svg>
この方法ではブラウザのスタイルシート切り替え機構でユーザーが任意にスタイルを切り替えることができてしまうが,フレーム内でsvgを表示している場合は比較的無難な方法と言える.だが残念ながらchromeとoperaで動作しない.
xml-stylesheet処理命令を操作し,cssファイルの参照先を変更する.この方法は先ほどと異なりブラウザからスタイルを変更することは出来ないが,xml処理命令オブジェクトを取得するにはいささか面倒な手順を踏む必要があるため,使い勝手はさほど良くない.更にoperaにおいて正しく動作しないため,この方法はあまりお勧めできない.
下記の例ではスクリプトを使って「cssa.css」から「cssb.css」にスタイルシートの参照先を切り替えた例である.rect要素の塗り潰しが,redからblueに変化していれば正しく動作している.
<?xml version="1.0" standalone="no"?>
<?xml-stylesheet href="cssa.css" type="text/css"?>
<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">
<rect x="20" y="20" width="160" height="160"/>
<script>
window.onload = function(){
//既存のxml-stylesheet処理命令にアクセスするには
//[1]document.styleSheets.item(0)でStyleSheetオブジェクトを取得し,
//[2]ownerNodeプロパティからProcessingInstructionオブジェクトを参照する.
document.styleSheets.item(0).ownerNode.nodeValue = 'href="cssb.css" type="text/css"';
};
</script>
</svg>
新たにstyle要素を生成し,@import規則でcssファイルを読み込むもの.先ほどのxml-stylesheet処理命令を書き換えるのではなく,新たにcssを読み込むことで設定を上書きしてしまう.この方法は新たなapiを憶える必要がなく,firefox,chrome,operaの何れのブラウザでも動作するため,通常はこちらの方法を選択するのが良いだろう.
<?xml version="1.0" standalone="no"?>
<?xml-stylesheet href="cssa.css" type="text/css"?>
<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">
<rect x="20" y="20" width="160" height="160"/>
<script>
window.onload = function(){
//style要素を生成し,文書に挿入する.
var style = document.createElementNS("http://www.w3.org/2000/svg", "style");
style.textContent = "@import url(cssb.css)";
document.documentElement.appendChild(style);
};
</script>
</svg>
しかしこのままではie9では正しく動作しない.この問題を回避するには,style要素を追加してから中身を書き換えるようにする.ieは古くからdomに追加されていないstyle要素の中身は無視されてしまうのだ.
<?xml version="1.0" standalone="no"?>
<?xml-stylesheet href="cssa.css" type="text/css"?>
<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">
<rect x="20" y="20" width="160" height="160"/>
<script>
window.onload = function(){
//style要素を生成し,文書に挿入する.
var style = document.createElementNS("http://www.w3.org/2000/svg", "style");
//先にスタイルを設定してから中身を書き換える.
document.documentElement.appendChild(style);
style.textContent = "@import url(cssb.css)";
};
</script>
</svg>
document.documentElementメソッドで得られるSVGSVGElementには描画に関わる多くのメソッドが提供されている.描画を遅延させたり,描画を強制したりする他にアニメーションを制御することができるなど,興味深い内容となっている.多数のオブジェクトを操作する際,再描画を一括で行いたい場合に効果的と思われる.その他,矩形範囲における要素の包含チェック処理など,使い方によっては便利な機能が含まれる(だが,未実装なものが多い).
alert関数,confirm関数,prompt関数を使い,モーダルダイアログを表示した場合でもanimate要素を用いたアニメーションはバックグラウンドで動作し続ける.これを止めたい場合はSVGSVGElementオブジェクトのもつpauseAnimationsメソッドとunpauseAnimationsメソッドを使ってアニメーションの実行を制御する.
animate要素にはアニメーションを制御するためのapi(TimeEventインターフェース)が定義されており,これを操作することでスクリプトからアニメーションの開始,終了を行うことができる.beginElement/beginElementAtメソッドはbegin属性に相当し,eneElement/endElementAtメソッドはend属性に相当する.At付きのメソッドは引数に指定した秒数が経過してから指定した動作が実行される.
プレゼンテーション属性以外の属性値(例えばx,y等)については対応するプロパティのanimValプロパティを参照することでアニメーション途中の値を取得することができる.
一方プレゼンテーション属性に関わるアニメーション途中の値はwindowオブジェクトの持つgetComputedStyleメソッドを利用することで取得する事が可能だ.このメソッドで得られたCSSStyleDeclarationオブジェクトは,元の図形要素のstyleプロパティから得られるものとは異なるオブジェクトとなる.
animate要素はアニメーション処理に伴い「beginEvent」「endEvent」「repeatEvent」イベントを発生させるので,これらのイベントをトリガーとしhtml要素を制御すると言った処理を実装することが出来る.タイマー処理の代替や,様々な処理の同期を行うのに活用できそうだが,いかんせんchromeで動作しない.
この例ではアニメーションが繰り返される毎に以下の内容を書き換えている.0回繰り返した.
次の例はsvgで時計を作ったものである.animate要素で事前に時計の動きを定義し,スクリプト側ではアニメーションの経過時間を設定するようにした.このようにjavascriptとanimate要素とを組み合わせることで, 複雑となりがちなスクリプト部を短くまとめることが出来る.
webブラウザでのアニメーション処理はcssのanimateプロパティを使ったものやsmilを応用したもの等,現状でも様々なパターンのものが存在している.その際,これらの処理とjavascriptとの処理を同期させる為のapiが現在検討されており,現状でも一部のブラウザにおいて限定的に利用可能である.またこのapiはアニメーション処理に特化したものとなっているため,利用することでバックグラウンドに隠れた際のリソースのコストを低減し,バッテリー消費の削減を見込むことができると有る.
例を示す.同期したいアニメーションの間隔は5秒なので,getCurrentTimeメソッドでアニメーション開始からの時間を取得し,それを5で割った余りが現在位置を計算する上での基準時刻となる.なおrequestAnimationFrame関数未実装のブラウザに対しても同様の動作をさせる場合は,setTimeout関数を用いて動作をエミュレートさせるようにすると良い.
※getCurrentTimeメソッドはブラウザ毎に挙動が異なるため,利用には注意を要する.(animate要素の有無,smilアニメーションの有無で挙動が変化する.)
なお,最終的にどのような形となるかはまだ分からないので,この機能を利用する場合はそのリスクを踏まえた上で利用すること.
以下に主なdomのapiを示す.スクリプトでどのようなことができるか前もって眺めておくと後で何かと便利だ.詳しくはw3cによるsvgの仕様の他にこちらもしくはこちらから調べることが可能だ.htmlにおけるdomの知識だけでもある程度のことは実現できるが,svgを自在に操るには専用のapiを知っておいたほうが良い.ただ,svgのapiの詳細に関するドキュメントが少なく,多分に手探りのプログラムが必要となってしまう.しかも元々の仕様に曖昧さが残るため,ブラウザ毎に各メソッドの挙動は驚くほどバラバラであるし,未実装なものも少なくない.簡単なプログラムでさえ単一の環境のみで動作を確認するのは非常に危険である.
svgにおいても各種イベントを利用することが出来る.イベントはスクリプト実行のトリガとすることが出来る他,アニメーションの実行トリガに指定することが出来る.通常rect要素などの図形を定義する要素によってイベントは生成され,それ以外の例えばg要素等はこれらの処理には関係しない.が,アニメーションに関わるイベントなど一部はそれ以外の要素が発生するものもある.但しイベントごとに実装状況が異なるので,十分な動作検証が必要である.以下は仕様書より抜粋したものでdomイベントについては記述を省いた.
keydown,keyup,keydown等のキーイベントはsvgの仕様そのものには定義されていないため利用することができない.例えば「<svg onkeypress="alert('')">…</svg>」といったコードは動作しない.その一方,windowオブジェクトでキーイベントを捕捉するスクリプトは正しく動作するので,こちらで代用する.html5の埋め込みsvgであればhtml要素でキーイベントを取得するようにする.
html埋め込みのsvg要素の場合はhtmlに準じたイベントが発生するため,これらに加えてドラッグドロップに関わるイベントが発生する.
同様に環境によってはタッチイベントが発生しうる.これはiPhone等のiOS系の環境で実装されているイベントである.(※とりあえず掲載だけしています)
スクリプトは様々な使い途があるが,ここでは有効な使い途についてのヒントとなるようなトピックスを紹介する.
ここまでsvgdomをjavascriptで直接操作する方法を見てきた.これだけでも様々なスクリプト処理を行えることとなるが,いかんせん提供されている機能が貧弱であり,コードの記述量が増加する傾向にある.従って,何らかのフレームワークを導入してコーディングの負担を減らしたいところだが,svgに対応したライブラリには既に様々なものが存在する.何れも得意とする分野があるため,慎重に選択したい.
その他jQuery等のjavascriptライブラリによってはsvg用のアドオンが提供されているものもある.使い方に慣れているライブラリを使ってsvgを操作できるため,場合により導入を検討しても良いだろう.それでも,100%本来のライブラリの機能を利用できるとは考えないほうが良く,svg独自の処理が必要となるケースがある.
htmlドキュメント内部でスクリプトを実行するにあたり,動作環境となるブラウザがsvgの描画に対応しているかを判定したい場合がある.このような場合は次のようにすると良い.
if(!window.SVGSVGElement){
//svgをサポートしない場合
}else{
//svgをサポートする場合
}
機能毎の実装状況を確認する場合はdocument.implementation.hasFeatureメソッドを利用する方法もある.これはswitch要素で行った処理をscriptで行なっていることになる.なおsvg1.0と1.1とで書式が異なるため,使い勝手はよくない.またブラウザの自己申告値なので,はっきり言って信用ならない.