Snap.svgについて
Snap.svg にようこそ.ここではまず始めに「Snap.svg」を扱う上での基礎知識について解説します.
Snap.svgとは
Snap.svgはwebブラウザ上にベクタグラフィックを描くためjavascript(ecma script)フレームワークです.Illustrator等のグラフィックツールで有名なAdobe Systems社がオープンソースとして提供しています.グラフィックの描画にHTML5で正式に採用されたSVG(scalable vector graphic)を使っており,環境に依存しない美しい出力結果が得られます.
Snap.svgはSVGが元々備えているapi(SVGDOM)をラップすることで,冗長となりがちなスクリプト記述を簡潔に行える他,グラフィックを描く上で役に立つ機能を多数実装しています.かつて同様のライブラリとしてRaphaël.jsがありましたが,このライブラリではsvgに特化することにより,より高度なグラフィック処理を行えます.
なおこれらのモジュールは同じ作者であることから,Snap.svgは広い意味でRaphaël.jsの後継ライブラリと言えるでしょう.強力なパスの編集機能はSnap.svgでも健在です.とは言え,現状ではまだバージョンも低い上,細かい部分の詰めが甘いなど完成度はお世辞にも高くはありません.よって安心して使えるツールとは言えないのが歯痒いところです.今後の発展に期待しましょう.
本ページの目的
このように今後の発展が楽しみと言えるSnap.svgですが,公式のドキュメントがあまり役に立ちません .api表 にしても,ライブラリ開発者向けの簡素なもので,実際にどのような機能を提供しているのかを読み取ることが出来ません.
そこで本ページはSnap.svgが提供しているapiをソースコードからつぶさに調査し,その使い方及び応用について出来る限り判りやすく解説することを目的に作りました.またSnap.svgはオープンソースソフトウェアですから,足りない・不具合のある機能を自由に追加・修正することが出来ます.その際の参考資料としても価値あるものなるでしょう.
とは言え,内容をより正しく理解するにはSVGやjavascriptの仕組みをある程度知っている必要があり,Snap.svgをマスターするのは一筋縄で行かない点にご注意ください.
Snap.svgの機能と役割
Snap.svgが提供する機能について見る前に,なぜSnap.svgが必要となるのかについて考えてみましょう.
Snap.svgの必要性
SVGDOMはSVG文書をプログラムから操作するためのapiを定めたものです.今日のHTML5のサポートを謳ったwebブラウザであればこのSVGDOMをサポートしているので,javascriptから自由にSVGグラフィックを描くことが可能です.しかし,このapiセットは必要最低限度のものしか定義されておらず,お世辞にも使い勝手が良いものではありません.
例えば下は四角形を一つ表示するスクリプトです.SVGDOMを直接操作した場合,このような簡単な処理にもかかわらず10行近いスクリプトを記述する必要があります.
このようにSVGDOMを直接操作した場合,実現したい内容に対するコード記述量が余りに大きく効率的ではありません.そこで上記のコードをSnap.svgを使って書き換えると次のようになります.
驚くべきことに1行で済んでしまいました.
実際にはより複雑なグラフィックが要求されますから,処理の内容も相応に大きなものとなるでしょう.その際,SVGDOMを直接扱った場合とSnap.svgを導入した場合とで,記述すべきコードの量において雲泥の差が発生することは明らかです.一般に,プログラムのコード量は実際のコーディング作業のみならず,後々の保守工程でのコストに直接的に関係するため,可能な限り短く簡潔なものが良いとされています.この面でSnap.svgは劇的な効果を発揮します.
Snap.svgが提供する機能
以下はSnap.svgが提供している機能です.
煩雑なDOM操作の簡略化
先ほど見た通り,SVGグラフィックでは様々な要素・プロパティ・APIを多量に扱います .そのため,簡単な処理を記述するだけでも非常に多くの記述が必要となります.Snap.svgではこれらをより簡便かつ直感的に扱えるようにしています.
メソッドチェーンによるコードの簡潔化
処理を連続して記述することでコードの可読性を向上します.これはjQuery等でおなじみの仕組みでしょう.確かにメソッドチェーンによる弊害(デバッグ可能性の低下等)も考えられますが,コード記述量の圧縮に依るメリットを鑑みれば些細な問題と言えるでしょう.
強化されたSVGコードの入出力機能
SVGコードの挿入・抽出を行うための機能を提供します.例えばHTMLにおけるinnerHTMLプロパティと同等の機能を使えば,より直感的なコードの記述が可能です.またAjax機能による外部リソースの埋め込みも可能です.
様々なユーティリティー機能
色の相互変換を行ったり,文字列のフォーマット機構等,web開発に有効な仕組みを多数備えています.
強力過ぎるパスの編集機能
パスを編集するための高度な仕組みを提供します.これだけでもSnap.svgを導入する価値があります .
フィルタ・マスク・クリップ処理のためのapiを提供
これらの機能をsvgで定義した場合,往々にして記述が複雑になりますが,Snap.svgでは予め専用のメソッドが定義されているため簡単に呼び出すことが出来ます.
強化されたイベント通知機構
様々なイベント処理を簡潔に記述できます.また,自前のイベントを作ることも可能です.
アニメーション機能
SVGの標準機構(SMIL)に代わる独自のアニメーション機能を提供します.様々なレベルでのAPIを公開しているので,目的に応じ自身でフレームワークを構築することも出来ます.
Ajax機能による外部グラフィックデータの読み込み
Snap.svgは既存のSVGグラフィックを非同期的に読み込んで自由に操作可能です.上手く活用すればドローツールなどに依るグラフィック制作とプログラミング設計作業とを分離することが出来ます.
優れた拡張性拡張性
Snap.svg自体がプラグインの集合的な構造を採っており,使う側からも自由に拡張できます.また,コードそのものも比較的素直な構造ですから,機能の修正や追加も容易です.
つまりSnap.svgを導入することで,より自在にSVGを扱えるようになるのです.
Snap.svg導入に関わる注意点
その一方でSnap.svgを導入することに依るデメリットも検討しておくべきでしょう.
Snap.svgはスクリプトの動作パフォーマンスを考慮していません
Snap.svgを導入した場合,SVGDOMを直接操作した場合と比べ,(その程度はともかく)確実に動作パフォーマンスが低下します .これはSnap.svgが必ずしも効率的に動作するように設計されているわけでないことに起因し,ある意味利便性とのトレードオフと言えるものです.
従って,動作速度をより重視する環境においては無理にSnap.svgを適用するのではなく,利用するapiを制限するなどの工夫が必要となるでしょう.Snap.svgを使わずともSVGDOM(及びHTML5)が提供するapiは多岐にわたっており,意外に何とかなるケースは多いものです.
Snap.svgはブラウザ間の互換性を担保しません
またRaphaël.jsが目標としていたブラウザ間の互換性を保つ目的はSnap.svgにおいて大分薄れています.SVGの動作はCSSと同様に環境ごとの差異が大きく,ライブラリ側でその互換性を保つのは機能的な面からも困難 です.そのため,Snap.svgを使う側においても「そのようなものだ」と割りきって使う必要があります.(Snap.svgを導入してもクロスブラウザでの動作検証コストは変わりません)
Snap.svgはjQueryの影響を受けているため,様々な面でjQueryライクな記述・機能が使える反面,HTMLとSVGの仕様におけるインピーダンスミスマッチから独自の機能も多数含まれています.そのため,Snap.svgの学習曲線はそれなりに高いものの,jQueryに精通している方ほどSVG独特な挙動に悩むかもしれません.
Snap.svgはHTMLに依存した機能を提供しません
Snap.svgはあくまでSVGDOMの機能を強化する事を目的としており,それ以外の例えばキー入力等の(SVGに存在しない)HTML仕様に特有な機能には全く関与していません.従って実際にはjQuery等のHTMLに特化したライブラリと組み合わせて利用することとなるでしょう.その結果,二つの思想を異とするライブラリを同時に操作せねばならず,自ずとスクリプトの内容が複雑化してしまいます .
ごく簡単な操作であればjQueryからSVGを操作することも出来なくはありませんから,Snap.svgを導入する必要性についてよく考える必要があるでしょう.
競合するライブラリとの比較
昨今はSVGによるグラフィック描画を主体としたjavascriptライブラリが色々と発表されていますが,その代表格として「d3.js 」「Two.js 」「SVG.JS 」が挙げられます.ではその中でSnap.svgの特色とはなんでしょう.
それはSnap.svgがSVGをより高度かつ容易に操作すること を主眼に作られている処です.穿った見方をすればSVGDOMを拡張しているだけとも言え,時には物足りない部分もあります.が,機能が絞られている分学習しやすかったり,SVGを扱う処理であれば大抵のものと相性が良いといった利点があります.特にユーザーインターフェースの作成においてその真価を発揮します.
他方のd3.jsではデータのグラフィック表現に特化しており,SVGのサポートはあくまでその手段に過ぎません.よって,高度な処理を実現するにはSVGDOMの力を借りる必要があり,しばしばそれは非常に煩雑なものとなります.とりわけd3.jsではフレームワーク的な考え方が強制されるため,使いこなせるようになるにはそれなりの学習コストを見込む必要があります.また,Two.jsはHTML5におけるグラフィック描画api(SVGDOM,canvas要素,webgl)に対する共通ラッパーを提供し,利用者側の学習・移行コストを軽減する目的で設計されています.よって利用可能な機能に制約が付くなどのSVGの表現力を削ぐ側面を併せ持っています.
どちらにせよSVGそのものに関心がある訳ではありませんから,この点に於いてSnap.svgは抜きん出ていると言えるでしょう.このようにライブラリ毎に役割が異なるため,用途に応じて適切に選択する必要があり,場合によってはそれぞれを組み合わせて利用することも十分検討に値します.
ちなみにSVG.JSはこれらの中では最もSnap.svgに近い立ち位置にあるライブラリと言えますが,Snap.svgは前述のRaphaël.jsでの成果が取り入られている分多彩な機能が利用できます.とは言え,後は好みの問題ですから,実際に触ってみた上で扱いやすい方を選択すればよいでしょう.
ファイルの入手先
Snap.svgの安定版はSnap.svgの公式サイト から入手できます.リファレンスや動作サンプルも同梱されています.
なお,最新の開発版コードはgithub において公開されています(現在ver0.2.1).安定版には存在しない,様々な機能強化がなされていますが,安定には程遠いため参考程度に留めましょう.
なお,要望・不具合の報告は上記githubの他,twitter でも受け付けているようです.
この他npmから導入する方法もあるようですが,バージョンが古い可能性があります.
動作環境
このようにSnap.svgは内部でSVGを用いていますから,少なくともSVGを解釈可能なwebブラウザを用意する必要があります.従って,次のブラウザでは動作しません.
Internet Explorer 8以前の環境(もしくはその互換環境)
Android2.x以前の環境の標準webブラウザ
なおブラウザ毎のSVGのサポート状況によっては動作しない機能もありますから,気をつけて下さい.
SVGの利用形態
また,SVGの利用形態にも制約があります.Snap.svgを正しく動作させるには,SVGを(X)HTMLにおけるインラインSVGグラフィックとして扱うか,もしくはSVG文書をHTML文書からobject/iframe/embed要素経由で埋め込んでおく必要があります.
なお,SVGではHTMLと同様にscript要素から外部のjavascriptライブラリを参照出来ますが,SVG文書から直接Snap.svgを読み込ませた場合,細かなapiの違いから正しく動作しません.
SVGグラフィックをimg要素や背景として利用している場合
Snap.svgはあくまでSVGDOMの拡張ですから,img要素等からSVGグラフィックを表示した場合などの操作可能なSVGDOMが存在しないケースには対応できません.ですが,擬似的に中身を操作することは可能です.詳しくはAjaxの項で解説します.
バージョン8以前の古いIEも動作対象としたい
残念ですがこの用途でSnap.svgを使うことは出来ません.なお,利用可能な機能が制限されても良ければRaphaël.js が利用できます.この場合,厳密にはSVGを使ったことにはなりませんが,ベクタグラフィックを描くと言った目的は十分に果たせます.
Android2.xも動作対象としたい
残念ですがこの用途でSnap.svgを使うことは出来ません.その代わりにHTML5要素のcanvas要素を使ってSVGを描くライブラリcanvg を検討して下さい.利用できる機能が大幅に制限されてしまいますが,単純なグラフィックを描くだけであれば事足ります.
何れにせよSVGやHTML5による強力な表現力を捨てる事になりますから,その必要性についてはよく検討して下さい.
ライセンス
Snap.svgはApacheライセンスver2.0 にて配布されており,自由に利用することが出来ます.
目を通しておきたい資料
Snap.svgが動作するwebブラウザ環境では現在「SVG 1.1 second edition」と呼ばれる仕様に基づいています.従って,Snap.svgを扱う場合もこの仕様を基準に考えましょう.また今後SVGはSVG2と呼ばれる次世代の仕様へと移行していきます.現状では動作しない機能がほとんどですが,こちらの動向からも目が離せません.
この他にもSVGDOMやHTML5のapiを用いたスクリプトコードサンプルはSnap.svgにおいても有用です.
チュートリアルなど
Snap.svgの動作サンプルについては本サイトの他にも幾つかあるようです.
Snap.svgの導入
Snap.svgの概要について眺めたら実際に動かしてみましょう.
モジュールの組み込み
入手したSnap.svgのアーカイブを展開すると中にdistフォルダがありますので,その中の「snap.svg-min.js」もしくは「snap.svg.js」を使いやすい場所にコピーしておきます.
このファイルをhtml/xhtml文書から参照します.例えば次の1行をhead要素に追加します.
]]>
モジュールファイルの参照先については,お手持ちの環境毎に内容を書き換えて下さい.
snap.svg-min.jsとsnap.svg.jsとの違い
snap.svg-min.jsはsnap.svg.jsの記述を圧縮し,ファイルサイズを抑えたもので,機能的には全く同じものです.スクリプトの開発中はエラーの発生箇所が判りやすいようにsnap.svg.jsを用いて,実公開環境では通信量を抑えるためにsnap.svg-min.jsを使うと言ったように使い分けると良いでしょう.
とは言え,現状の完成度を鑑みるに,snap.svg-min.js特有の不具合が含まれていたり,元のコードに手を入れざるを得ないケースも多々あり,実際の処snap.svg-min.jsを利用するのは現実的ではありません.
SVGをサポートする環境でのみSnap.svgを組み込む
SVGをサポートしない環境でSnap.svgを実行した場合,利用可能なapiの違いからエラーが発生してしまいます.このエラーを無視することが出来ないのであれば,下記のようにDOMのSVGサポート状況をチェックしてから動的にSnap.svgを読みこむようにします.
if(document.createElementNS
&& document.createElementNS("http://www.w3.org/2000/svg", "svg").viewBox){
var scr = document.createElement("script");
scr.src = "snap.svg-min.js";
scr.onload = function(){
//Snap.svgロード後の処理
}
document.head.appendChild(scr);
}
]]>
動作原理は次のとおりです.
レガシーIEにはdocument.createElementNSメソッドが存在しません.
Andoroid2.x以前のブラウザではcreateElementNSメソッドで生成したsvg要素がSVGSVGElementインターフェースを実装しません.つまりviewBoxプロパティが存在しません.
SVG未サポート環境への対処策は個々のプロジェクト毎に異なりますから,ここでは踏み込みません.
動作の確認
Snap.svgの組み込みが完了したら,動作の確認をします.先程の文書において,ライブラリの参照を指定したscript要素よりも後に下記のコードを記述し,webブラウザで開いてみましょう.
]]>
このようにグラフィックが描かれたらSnap.svgが正しく動作しています.上手く行かない場合は,デバッグツールなどを用いて正しくモジュールが読み込めているか,コードの内容に不備がないか確認して下さい.
Snap.svgを活用するコツ
このままSnap.svgを使い始めても良いのですが,より効率的に開発を進めるために次の点を念頭に入れておきましょう.
初心に帰ろう
あなたがこれから取り組もうとしている技術はSVGであり,HTMLとはちょっと異なるものです.そのため,HTMLでの常識がSVGにおいて通用しないこともあり得ます.従って思い通りの結果にならない場合は闇雲に解決策を探すのではなく,SVG仕様を確認しましょう.もしかしたら些細なミスで動作していないだけかも知れません.
SVGの機能を理解しよう
SVGではHTMLと異なり,様々な要素を組み合わせることで目的を果たす 傾向が強く,SVGを活かすにはこの上手な組み合わせレシピをたくさん知っている必要 があります.あくまでSnap.svgはこのレシピを効率的に作るための道具に過ぎませんから,一見遠回りかもしれませんが様々なサンプルコードを読み解いて,SVGそのものを理解することが結局はSnap.svgの習熟に繋がります.
SVGの構造を意識しよう
Snap.svgでのグラフィックの処理結果は全て文書中にSVGツリーとして構成されます .従って,意図しない動作が発生した場合はソースコードのみならず,ブラウザのもつDOMインスペクタを使い,どのようなSVGが生成されているのかを確認しましょう.
Snap.svgのコードを確認しよう
Snap.svgはまだ現在進行形のモジュールです.従ってモジュールそのものにバグが存在する 場合があります.出来ればSnap.svgそのもののコードも調査対象とし,時にはライブラリそのものに手を入れる必要があるでしょう.またライブラリのコードを読むことで内部構造を理解できれば,より効率的なコードを記述できるだけでなく,機能を追加することも可能となるでしょう.実際,Snap.svgに機能を追加するのは簡単です.
ブラウザの動作傾向を意識しよう
SVGはブラウザごとの動作の差異が大きいことで知られています.これはSnap.svgにおいても言えることで,生成したSVG構造は正しいものの,特定の環境でのみ不具合が発生するということはよくあります.従って,上手く動作しない場合はブラウザそのもののバグも疑うべきです.そのため,動作検証は複数の環境で行うことをお勧めします.
グラフィックの構造を設計しよう
Snap.svgがサポートしているからと言って,グラフィックの全てをスクリプトで構築すると言ったやり方はおすすめしません.html文書と同様に,大枠となる構造はsvg要素として定義 しておき,必要に応じてその中身を操作すると言ったように役割を分担させるようにしましょう.こうすることで,SVGとスクリプトの問題の切り分けが容易になります.また,こうすることでドローツールで作ったSVGグラフィックとスクリプトとを分離することが可能となり,並行作業が容易になります.
なお,本ページでは説明の都合上,処理の全てをコード内部で済ませていますが,常にこの通りにすべきというわけではありません.
筆者の経験上,SVGのデバッグは総力戦となりがちです.W3C仕様の漏れ,個人的な仕様の捉え間違い,ブラウザの動作不良,ライブラリのバグ,スクリプトのミス等あらゆる部分に落とし穴があると言ってもよく,一筋縄では行きません.そのため,将来的なSVG資産とするためにも地道な情報・知識の収集・蓄積が重要となるでしょう.
補足)本ページにおけるサンプルコードについて
本ページに掲載されているサンプルコードはこのページで実際に動作しているものです .従って,動作させるブラウザ環境によって微妙に出力結果が異なる 場合があります.そのため,Snap.svg導入を検討する前の動作検証用 としてもお使いいただけます.
また,コードの右上の編集 ボタンをクリックすると簡易エディタパネルが表示され,実際にSnap.svgのコードを編集し実行させることが出来ます .
なお,コード中のgetContainer,getDisplayメソッドはスクリプトの内容を表示するためのサポート機能であり,Snap.svgとは関係ありません.
サンプルコードに適用されるライセンスについて
基本的にコードそのものは自由に扱って構いませんが,記載されたコードに対するコピーライト・ライセンスを考慮する場合はSnap.svgと同等のApache License 2.0が適用されるものとします .本ページからコピーペーストした旨を記述し,後はご自由にお使いください.
Snap.toString()
Snap.toStringメソッドはSnap.svgのバージョンを返します.
Snap.svgの構造
Snap.svgを使いこなすために,まずその全体像を見てみましょう.
Snap.svgを構成するオブジェクト群
Snap.svgはSVGDOMをラップする複数のオブジェクトから構成されています.ここではそのオブジェクト毎の役割についてまとめてみました.
Snapオブジェクト
Snap.svgの名前空間を表し,様々なユーティリティー機能を提供するオブジェクト.
Paperオブジェクト
グラフィック本体に相当するオブジェクトです.各種図形を描くための機能を提供します.なおPaperオブジェクトはElementオブジェクトでもあり ,svg要素に対応します.
Elementオブジェクト
SVGの各種要素に対応するオブジェクトです.SVGDOMを操作するための様々な機能を提供します.
Setオブジェクト
Elementの集合に相当するオブジェクトです.スタイルの一括設定や,繰り返し処理などを管理します.
Fragmentオブジェクト
SVGグラフィックの一部分を表すオブジェクトです.XMLDOMのdocumentFragmentオブジェクトに相当します.
Matrixオブジェクト
座標変換を行う際に利用する行列に相当するオブジェクトです.行列操作に関わる各種機能を提供します.
Animationオブジェクト
アニメーション設定を表すオブジェクトです.
mina/animオブジェクト
Animationオブジェクトの設定を元にアニメーション処理を行います.
eveオブジェクト
Snap.svg独自のイベントトリガー機構です.
この内,グローバル変数として登録されるものはSnap,eve,minaの3つです.他のjavascriptライブラリを利用する場合やグローバル変数を定義する際は,名称が競合しないようにして下さい.
以下これらの動作について説明していきますが,必ずしもオブジェクトごとに説明していくわけではありませんのでご注意下さい.
この分類は筆者独自のものです.公式サイトでのドキュメントでは特に言及はされていません.よって今後変更となるかもしれません.
グラフィックを描画する流れ
Snap.svgを使ったグラフィック描画処理は概ね次の流れに沿って行います.
スクリプトの実行トリガーを決めます.
何らかのイベントに対してグラフィック描画処理を登録します.例えば次のイベントが候補に上がるでしょう.
windowのloadイベント
最も一般的なタイミングです.文書の全ての描画が完了した上でスクリプトが実行されるため,安定した結果が得られます.その代わり,余計な再描画処理が発生するため動作パフォーマンスに劣ります.出来ることなら下のDOMContentLoadedイベントを利用しましょう.
documentのDOMContentLoadedイベント
DOM解析が完了したタイミングで実行されるため,余計な再描画処理が発生しにくく動作が軽快になります.特に問題が無い限りはこの方法が良いでしょう.
Raphaël.jsやjQueryなどにあったDOMContentLoadedイベントに対するショートカットは存在しないようです.
clickイベント等
任意のタイミングで処理を実行します.全ての処理をこの中に記述してもよいですが,イベントが発生する頻度を鑑みて,予め事前処理を行っておく等の工夫をしましょう.
script要素のロードタイミング
この場合は,必ずSnap.svgを読み込んだ後で自作のスクリプトが実行されるようにして下さい.なお,DOM解析が完了していないタイミングでスクリプトを実行していますから,svg要素の取得や作成したsvg画像の挿入先等に制約が発生することになります.
またscript要素のdefer属性によってライブラリ読み込みの高速化を図っていた場合,上手く動かないケースが発生します.
Paperオブジェクトを取得します.
上記で登録した関数内部でPaperオブジェクトを取得します.以降このPaperオブジェクトに対してグラフィックの追加を行っていきます.
グラフィックの描画/外部グラフィックの読み込み
Span.svgのもつapiを元にグラフィックを定義・描画していきます.
Paperオブジェクトの取得
Paperオブジェクトを取得するには次の何れかを実行します.
Snap(width, height)
新たにSVGグラフィックを生成する場合に利用します.この例は先ほど示しました.svg要素を新たに生成し,body要素に挿入します.引数に与えたwidth
,height
はsvg要素のwidth,height属性に割り当てられます.
本メソッドを実行すると自動的に文書中にsvg要素が挿入されます.従って予期せぬエラーが発生した場合,その残骸が画面上に残ってしまいます .そのため,実際にはグラフィックを挿入したい部分に空のsvg要素を用意しておき,下のSnap(selector)
メソッドを用いたほうが良いでしょう.
Snap(selector)
文書内に既にsvg要素が存在している場合に利用します.引数として与えたセレクタ文字列を基にdomの内容を検索し,見つかったsvg要素に対応するPaperオブジェクトを生成します.レイアウト上固定された位置にSVGが存在する場合,もしくはSVG文書に対しSnap.svgを利用する場合はこの方法が便利です.
This svg's id is "svg_as_paper".
Snap(svgsvgelement)
SVGDOMのオブジェクト(SVGSVGElementオブジェクト)が入手済みの場合に利用します.引数として与えたオブジェクトをPaperオブジェクトでラップしたものを返します.iframeやobject要素内部のHTML/SVG文書を操作する場合に利用 します.
例えば,下記のようにobject要素のもつcontentDocument/contentWindowプロパティからサブSVG文書内部のsvg要素にアクセス可能です.
SnapメソッドにSVGを表示しているobject要素(HTMLObjectElementオブジェクト)を直接渡す方法もあります.type属性にコンテンツタイプ値image/svg+xml
が指定されていた場合,上記のようなコードを記述せずとも直接Paperオブジェクトを取得できます.が,ソースコードでは上記のようなブラウザごとの違いを考慮していないため,正しく動作しない場合があります.
Paperオブジェクトの特徴
Paperオブジェクトはsvg要素に対応するもので,後述するElementオブジェクトでもあります.従って他のElementオブジェクトにはない特色を持っています.
グラフィックを描画するためのメソッドを提供します.
これについては次のセクションで解説します.
Paperオブジェクトをネストすることが出来ます.
viewBoxプロパティによりグラフィックの描画単位を定義します.
Elementオブジェクトのメソッドの一部に無効となるものがあります.
最も外側のPaperオブジェクトではHTMLのスタイルが有効となります.
とは言え,正しく使いこなすにはSVGそのものに慣れておく必要がありますから,よく判らなければ読み飛ばして構いません.
ネストしたPaperオブジェクト
PaperオブジェクトをPaperオブジェクトに追加することが出来ます.これはsvg要素にsvg要素を挿入することにあたります.
この操作はグラフィックの描画基準を変更する場合に用いられます.なお,svg要素をネストするための専用のメソッドが提供されています.
Paper.svg(x,y,width,height,vbx,vby,vbw,vbh)
Paper.svgメソッドはsvg要素に相当するElementオブジェクト(つまりPaperオブジェクト)を生成します.vbx,vby,vbw,vbhはビューボックスに対応する値です.先ほどの例は次のように書き換えることが出来ます.
viewBoxプロパティの指定
viewBoxプロパティはグラフィック範囲に対する描画範囲と座標系を定義します.この例では実際の[200px×200px]のカンバスサイズに範囲[300×300]の描画範囲を割り当てています.
このプロパティはグラフィックの位置をスライド(パン)させる場合やズームする際に利用します.
図形の描画単位・座標軸を設定する
通常スクリーン上でsvgを描画する場合,単位としてpxが用いられます.が,次のようにwidth-height値とviewBox値とを合わせることで,グラフィックを特定の単位基準に描けます.下の例では単位mmを基準にpath図形を描いています.
無効となるメソッド
svg要素に作用しない属性値(例えばtransform属性)に対応するメソッドは無効になります.これはSnap.svgの不具合ではなく,svgの仕様に関わるものですから対処する方法はありません .
最も外側のsvg要素に対する操作
HTMLコンテキスト下にフローコンテンツとして配置されたsvg要素には,SVGでのスタイルに加えてHTMLでのスタイルが適用されます .従って次のようにstyle属性にfloat:right;
を指定する事が出来ます.
しかし,Snap.svgではSVGでのスタイルのみを扱うため,次のようにattrメソッドを介した操作は無効になります.
Paperオブジェクトと同様に振る舞うElementオブジェクト
Paperオブジェクトと同じように振る舞うElementオブジェクトには次のものがあります.何れもSVGではコンテナ要素に該当するものです.
svg要素に対応するElementオブジェクト
g要素に対応するElementオブジェクト
pattern要素に対応するElementオブジェクト
mask要素に対応するElementオブジェクト
これらは基本的にPaperオブジェクトが提供しているapiを自由に呼び出すことが出来ます.が,要素ごとに有効となる属性等が異なるため,細かな部分での使い勝手が変化する点に注意しましょう.
Snap.svgではこれらのオブジェクトを生成する際,apiをコピーすることでPaperオブジェクトとしての振る舞いを追加定義しています.
前置きはここまでとして,以下Span.svgが提供するapiについて解説していきます.
Paperオブジェクトによる描画の基本
PaperオブジェクトはSVGにおけるsvg要素に相当し,加えて基本的な図形の描画機能を備えています.
基本図形の描画
Paperオブジェクトを用いると円や矩形(四角形)等の基本的な図形を描画できます.何れも実行するとElementオブジェクトを返します.
Paper.circle(cx,cy,r)
Paper.circleメソッドは円を描画します.
json形式での描画
引数にjson形式のデータを渡すことも出来ます.スタイル情報を一括指定することが可能です.
Paper.rect(x,y,width,height,rx,ry)
Paper.rectメソッドは矩形を描画します.
rx,ryを与えると,角を丸めることが出来ます.
json形式での描画
引数にjson形式のデータを渡すことも出来ます.スタイル情報を一括指定することが可能です.
この方法はバウンディングボックスや矩形状のオブジェクトを描画する際に便利です.
特殊なプロパティ「r」
rectオブジェクトに対してプロパティ"r"を指定した場合,r1,r2属性に同じ値を指定したことになります.
Paper.ellipse(cx,cy,rx,ry)
Paper.ellipseメソッドは楕円を描画します.
json形式での描画
引数にjson形式のデータを渡すことも出来ます.スタイル情報を一括指定することが可能です.
Paper.line(x1,y1,x2,y2)
Paper.lineメソッドは線分を描画します.
json形式での描画
引数にjson形式のデータを渡すことも出来ます.スタイル情報を一括指定することが可能です.
Paper.polyline(points/varargs)
Paper.polylineメソッドは折れ線を描画します.引数には配列もしくは任意個の引数を与えることが出来ます.塗り潰しを必要としない場合はfill値に"none"を指定します.
json形式での描画
引数にjson形式のデータを渡すことも出来ます.スタイル情報を一括指定することが可能です.
Paper.polygon(points/varargs)
Paper.polygonメソッドは多角形を描画します.Paper.polylineと同様に引数には配列もしくは任意個の引数を与えることが出来ます.
json形式での描画
引数にjson形式のデータを渡すことも出来ます.スタイル情報を一括指定することが可能です.
パス図形の描画
より複雑な図形を描画する場合はPaper.pathメソッドを利用します.
Paper.path(pathString,segmentArray)
Paper.pathメソッドは任意のパス図形を描画します.
ここで引数に与えるパス文字列はSVGにおけるパス文字列 に準じます.
引数としてはパス文字列の他,Snap.parsePathStringメソッドから得られる配列データを渡すことが出来ます.
この配列データはpath文字列にSnap.parsePathStringメソッドを施すことで得られます.セグメントごとにパスを操作する場合に非常に重宝します.
json形式での描画
引数にjson形式のデータを渡すことも出来ます.スタイル情報を一括指定することが可能です.パス文字列はpath
プロパティに割り当てます.
パス文字列で使えるコマンド群
以下にSnap.svgで扱えるパスコマンドの一覧を示します.SVGでのパスコマンドに加え,Snap.svg専用のパスコマンドが使えます.
パスコマンドの一覧
コマンド
意味
絶対
相対
M m 位置のスキップ(x y)
Z z 図形を閉じる
L l 線分を引く(x y)
H h 水平線分を引く(x)
V v 垂直線文を引く(y)
C c 3次ベジェ曲線を引く(x1 y1 x2 y2 x y)
S s 連続した3次ベジェ曲線を引く(x2 y2 x y)
Q q 2次ベジェ曲線を引く(x1 y1 x y)
T t 連続した2次ベジェ曲線を引く(x y)
A a 円弧を引く(rx ry rotation large-arc-flag sweep-frag x y)
R r Catmull-Rom曲線(x y)†
O o 楕円(rx,ry)†
U u 円弧/扇型(r,start,end)†
†はSnap.svgの拡張コマンド
パス文字列の仕様はSVG2において強化される予定で,その中にCatmull-Rom曲線の描画も含まれています.しかし,Snap.svg互換とはならないかも知れません.
Snap.svg拡張パスコマンド
Snap.svgでは下記に示す「R」「O」「U」コマンドを利用可能です.何れもSVG標準のコマンドでは描きにくい図形を簡単に定義できます.
Rコマンド:Catmull-Rom曲線
Snap.svgではパス文字列の拡張としてコマンド「R」が追加されています.Mコマンドで起点を指定しRコマンドでなめらかに繋げたい座標を列挙します.
参考のため,実際のパス文字列を出力しています.内部ではCコマンドに変換されていることがわかります.
Oコマンド:楕円
Snap.svgではパス文字列の拡張としてコマンド「O」が追加されています.コマンド実行時の終点を中心とし,楕円を描きます.12時の方向から時計回りに一周します.
参考のため,実際のパス文字列を出力しています.内部ではAコマンドに変換されていることがわかります.
Uコマンド:円弧・扇型
Snap.svgではパス文字列の拡張としてコマンド「U」が追加されています.半径・開始角・終了角の順に指定します.角度は右方向を基準に反時計回りに増大します.Lコマンド,zコマンドと組み合わせれば,扇型を描画することも出来ます.
ここで開始角と終了角の順番を取り違えると目的の円弧が得られません.
拡張コマンドにおける補足
これらのコマンドは非常に便利ですが,Element.animateメソッド等で変形する際に,意図しない動きを取ることがあります.この場合,Snap.path.toCubicメソッドやSnap.path.toAbsoluteメソッドでパスの内容をSVG標準のコマンドで再構成することで解決するかも知れません.
テキストの描画
基本図形と同様の操作で文字列を描画することが出来ます.
Paper.text(x,y,string/stringArray)
Paper.textメソッドでは指定した位置を基準に文字列を描画します.引数の渡し方により動作が若干異なります.
文字列を渡した場合,text要素を生成してその中に直接文字データを挿入します.
文字列の配列を渡した場合,text要素を生成してその中にtspan要素を配列のサイズの分だけ生成 して文字データを挿入します.下の例では,自動生成されたtspan要素を抽出し,文字列の一部のスタイルを変更しています.
テキストの改行
この動作を応用すると手動ですがテキストを改行させることが出来ます.テキストを1行毎に区切り,配列としてPaper.textメソッドに渡し,生成したtspan要素のそれぞれに描画位置を指定します.
json形式での描画
引数にjson形式のデータを渡すことも出来ます.スタイル情報を一括指定することが可能です.
テキストの配置
テキスト配置の基準はtext-anchorプロパティとdominant-baselineプロパティで決定します.
text-anchor/dominant-baseline
start/hanging middle/hanging end/hanging
start/middle middle/middle end/middle
start/ideographic middle/ideographic end/ideographic
なお,dominant-baselineプロパティはブラウザによって有効となる値が異なるほか,フォントの種類や文字種により位置がずれる場合があります.
パスに沿ったテキストの配置
Element.attr({textpath: pathStr})
Paper.textメソッドで得られたtextオブジェクトにtextpathプロパティを指定するとパスに沿ったテキスト配置が行えます.
textpathプロパティにはパス文字列の他にSnap.pathメソッドで得られたpathオブジェクトを渡すことも出来ます.
この場合,Paper.textメソッドに渡すx,y引数の内容はパス上の位置が優先されるため無視されます.
画像の読み込み
Paper.imageメソッドを使うと,外部画像を読み込む事が出来ます.
Paper.image(src,x,y,width,height)
Paper.imageメソッドはsrcに指定した画像を描画します.DOMでは(SVGの)image要素(SVGImageElement)を生成します.
画像のサイズを省略した場合,画像のサイズで描画されます.
通常SVGでは画像の描画サイズを指定する必要がありました.
本メソッドは画像を静的なものとして表示します .外部SVG画像を読み込んだ後,その内容を書き換えたい場合はSnap.load/Snap.ajaxメソッドを用いてSVGのDOM構造をインポートするようにします.
json形式での描画
引数にjson形式のデータを渡すことも出来るはずなのですが,現在のバージョンでは正しく動作しません.
この問題は一旦空のimageオブジェクトを生成してからattrメソッドを実行することで回避できます.
src
とxlink:href
の記述間違いという本当に単純ミスなので,自身で修正しても良いでしょう.
より詳細な画像読み込み処理
Paper.imageメソッドが提供している機能はあくまで簡易的なものです.外部画像読み込みの際,読み込み完了・失敗の判定が必要な場合はSVGDOMのload/errorイベントを利用します.
画像ロード完了時に処理を実行する例
画像ロード失敗時に処理を実行する例
描画オブジェクトのグルーピング
これまで生成してきた描画オブジェクトをグループ化すると,複数のオブジェクトを一括して管理することが可能になります.
Paper.g(elem1, elem2, ...)
Paper.gメソッドは描画オブジェクトをグループ化するコンテナオブジェクトを生成します.
これはSVGのg要素に対応するもので,スタイルの一括指定,一括変形,及びレイヤ分け機構として様々な場面に応用可能です.詳しくはこちら を参照.
gオブジェクトの中身は後から追加することも出来ます.
Paper.group(elem1, elem2, ...)
Paper.groupメソッドはPaper.gメソッドのエイリアスです.
gオブジェクトの振る舞い
gメソッドで生成したElementオブジェクトはPaperオブジェクトのように振る舞います .従って次のようにPaperオブジェクトのもつapiを呼び出すことが可能です.
図形のスタック
上記に示したメソッドを実行していくとPaperオブジェクトに対応するsvg要素に図形要素が積み重なっていきます.SVGでは要素が現れた順に図形を描画していくので,この順番を変更すると図形の重なり合いを変更できます.例えば下の例では先に生成したcircle要素の前にrect要素を挿入し,グラフィックの上下関係を変更しています.
HTMLではz-indexプロパティを使ってオブジェクトの重なり合いを制御できましたが,SVGには現状これに相当する機能はありません.
グラフィックのクリア
Paper.clearメソッドを利用するとグラフィックの内容をクリアできます.
Paper.clear()
Paper.clearメソッドはPaperオブジェクトが内包しているsvg要素の中身をクリアし,グラフィックの内容を初期化します.なお,defs要素内の各種設定情報は削除の対象となりません.
このメソッドは既存のグラフィックもクリアしてしまいます.例えば次の例では既存のsvg要素からPaperオブジェクトを生成し,clearメソッドを実行しています.
click me!
Paper.clearメソッドは使うべきではない
Paper.clearメソッドは内部におけるElementオブジェクトの開放が不十分です .そのため本メソッドに頼って多量のDOMオブジェクトを生成・廃棄した場合,メモリ開放に伴う不具合を引き起こします.なるべくこのメソッドに頼らないコードとなるように工夫しましょう.
Elementオブジェクトに対する属性・スタイルの指定
Paperオブジェクトが生成した描画オブジェクトに対して様々なプロパティを設定することが出来ます.
Element.attrメソッドによる設定値へのアクセス
Element.attrメソッドは内部に保持しているSVGElementへのアクセッサです.複数の値を一括して設定できる等,SVGDOMで煩雑だった記述を大幅に削減できます.
Element.attr(key, value)
Element.attrメソッドはsvg要素に対する各種設定を行います.key値に対応した属性にvalue値を書き込みます.domでのsetAttribute,getAttributeメソッドに相当します.なお,プレゼンテーション属性を指定した場合は,自動的にstyle設定(style属性)に変換されます.
この使い方を何度も繰り返す必要がある場合,下のマップオブジェクトを使った一括指定を使いましょう.
Element.attr({key1:value1,key2:value2,...})
Element.attrメソッドの引数としてはkey-value形式のオブジェクトを渡した場合,値の設定を一括で行います.
svg属性名にはハイフン形式(xxx-yyy
)のものがあります.この場合はcamel形式(xxxYyy
)に書き換えるか,キー値を引用符で囲む("xxx-yyy"
)で囲むようにします.
この記法はjQueryやRaphaël.js等でおなじみのものです.
Element.attr(name)
Element.attrメソッドに属性名を渡した場合,属性に対応する設定値が得られます.
指定可能な属性・スタイル
Element.attrメソッドで設定・取得可能な値に制限はありません .SVG仕様に存在しない属性を設定した場合はグラフィック描画処理からは無視されます.
代表的なものについて示します.
x,y,width,height,rx,ry,cx,cy,points,d…グラフィックの描画位置,サイズに関わる属性です.
fill,stroke…グラフィックの塗り潰し,外形線の描画に関わる属性です.
stroke-dasharray,stroke-linejoin,stroke-linecap,stroke-miterlimit…外形線の描画の仕方に関わる属性です.
opacity,fill-opacity,stroke-opacity…グラフィックの不透明度に関わる属性です.
詳しくはこちら を参照して下さい.なお,全ての環境で仕様の通りに動作するわけではありません.SVGの動作は現状でもブラウザごとに大きなばらつきがあります.
処理の都合上独自の属性を定義する場合,SVG仕様に存在しない名称としましょう.
特殊な属性
以下はattrメソッド経由で指定・参照可能な特殊なプロパティです.
path:pathオブジェクトに対するパス文字列を設定・取得します.
textpath:パスに沿ったテキストにおけるパスを設定します.
text:textオブジェクトに対するテキスト文字列を設定・取得します.
viewBox:文字列の他,配列[x,y,width,height]を渡すことが出来ます.また,値を取得すると自動的にbox形式のオブジェクトが返されます.
viewBox属性に対する扱い
svg要素等のviewBox属性をElement.attrメソッドで扱う場合は,その入出力形式に注意して下さい.
Element.attr({viewBox: array})
viewBox属性に対しては配列データを渡す事が出来ます.
Element.attr("viewBox")
viewBox属性を取得するとbox形式のオブジェクトが返されます.
カスタム属性
このようにElement.attrメソッドは基本的にdomに対するアクセッサを提供していますが,カスタム属性の定義を行うことで,指定した属性値の設定・取得時に任意の処理を実行させることが可能です.詳しくはカスタムイベントの項を参照して下さい.
属性値の相対指定
座標や大きさを表す属性についてはElement.attrメソッドに指定する値を現在値を基準とした相対値で記述することが出来ます.
+=[value][単位]
現在値に指定した値を足します.※単位を指定することが出来ます.
-=[value][単位]
現在値から指定した値を引きます.※単位を指定することが出来ます.
*=[value]
現在値に指定した値を掛け合わせます.
/=[value]
現在値を指定した値で分割します.
例を示します.
補足)ピクセル値に依る属性値の取得
SVGでは様々な大きさを表す単位としてピクセルの他,パーセントやem値等を扱えます.しかしグラフィックを描く上ではピクセルでの値が欲しくなる場合があります.Snap.svgではこの用途のためにElement.attrに似たメソッドとしてElement.asPXメソッドを提供しています.
Element.asPX(attr,[value])
Element.asPXメソッドはこの要素が属しているPaperオブジェクトでの大きさを取得します.value値を省略するとattr値に対応する値をピクセル値で取得します.value値を指定すると,attr属性にこの値を指定した場合のピクセル値が返されます.(Elementの内容を書き換えません).※パラメータとして属性名を指定する必要があるのは縦方向と横方向とでスケールが異なる場合があるからです.
しかし,このメソッドは出来る限り使わないほうがよいでしょう.ソースコードを見た限り,特定の条件下でしか正しい値を返しません.更にプログラム作者が本来意図している動作が読み取れず,かえってバグの温床になりかねません.本メソッドを利用せずともviewBox属性などを上手く使えば大抵の処理は上手く行くはずです.
クラス指定に依るスタイルの指定
SVGでは属性値やattrメソッドによるものの他に,スタイルシートを介してグラフィックの内容を指定できます.Snap.svgではこの仕組みを補助する目的で各SVG要素のクラス値を操作するためのapiを備えています.
スタイルシートによるグラフィックの指定
SVGはHTMLと同様にCSSを用いたスタイル付け機構を備えています.SVGにおけるスタイルとはグラフィックの見た目,つまり色やパターンを指します(それ以外のグラフィックの配置や形状に関わるものを含みません).
SVGではCSSで記述可能な属性をプレゼンテーション属性と呼んでいます.
例を示します.予め文書内に次のようなstyle要素を記述しておきます(link要素を使って外部CSSファイルを読み込んでも構いません).ここではクラスclassA
とclassB
に対するスタイルを定義しています.なおfill,stroke,stroke-width
はSVGの要素に対してのみ有効なプロパティになります.
.classA{fill:red; stroke:orange; stroke-width:15px;}
.classB{stroke-dasharray:30;}
]]>
その上でこのクラス名を図形要素に指定すると,指定したスタイル(グラフィック)の内容で図形が描画されます.
こうすることでSVGの出力処理をグラフィックの構造を定義する部分(スクリプト)とグラフィックの見た目を制御する部分(スタイルシート)とに分離できます.するとコードが分割されたことによりそれぞれの内容が簡潔となり,後々のコードの再利用やメンテナンスがやりやすくなります.
なお(X)HTMLにおけるインラインSVGではHTMLに対するスタイルシートとSVGに対するそれを混在して記述することが出来ます.そのため,CSSの管理コストが上がると言ったデメリットも存在するため,事前にきちんとCSSの設計・管理しておきましょう.可能であればscoped
属性を使ってスタイルの有効範囲を狭める方法もあります.
クラス値の制御を行うapi群
先程の例ではattrメソッドを使ってclass属性を指定していましたが,一般にclass属性には複数のクラス名を記述してスタイルを重ね掛けできます.従って内容の一部のみを追加・削除する場合はclass属性の内容を編集しなければなりません.Snap.svgではこの作業をサポートするメソッドが定義されています.
Element.addClass(className)
Element.addClassメソッドは指定したクラスを要素に追加します.複数のクラスを指定する場合はメソッドを複数回実行します.
Element.removeClass(className)
Element.removeClassメソッドは指定したクラスを要素から削除します.
Element.toggleClass(className)
Element.toggleClassメソッドは指定したクラスの適用状態(ON/OFF)を切り替えます.
Element.hasClass(className)
Element.hasClassメソッドは指定したクラスが現在この要素に適用されているかどうかを判定します.
これらのメソッドはjQueryから輸入されたものです.因みにjQueryをSVG要素に適用した際,class設定に関わるメソッドに失敗します.これはSVGDOMにおいてはSVGElement(正しくはSVGStylable)におけるclassNameプロパティがSVGAnimatedStringオブジェクトとして定義されているためです.HTMLElementではDOMString(つまり一般的な文字列)として扱われているため,これを前提としたjQueryはSVGに対して正しく動作しないのです.
スタイルの優先順位とElement.attrメソッド
SVGでは色などのスタイル情報を複数箇所で指定することが出来ますが,その優先順は次のとおりです.
style属性
SVG要素のstyle属性として直接列挙されるもの.最も優先順位が高い.
get/setAttribute("style",...)メソッドの他,SVGElement.style.xxxからアクセスすることができます.
style要素/link要素によるcssファイルの外部参照
style要素内で定義したものです.
プレゼンテーション属性
SVG要素の属性として直接記述したものです.上記のstyle属性/要素によるスタイル指定が存在しなかった場合に有効になります.
get/setAttributeメソッドから操作します.
Element.attrメソッドによるスタイル指定はプロパティの内容によりstyle属性に指定されたり,プレゼンテーション属性として指定されたりします.そのため,style要素によるスタイル付けを併用した場合,見かけ上正しく動作していないように見えることがあります .
例を示します.ここではclassCというfillプロパティとstroke-widthプロパティを指定するクラスを定義しています.
.classC{fill:red;stroke-width:5px;}
]]>
その上でこのスタイルの内容を上書きするようなコードを記述してみましょう.
このようにstroke-widthプロパティの内容は上書きできているのにfillプロパティはred色のままです .これはfillやstrokeなどのプロパティはSnap.svgにおいてプレゼンテーション属性に格納される ことに起因します.そのため,classCによるfill値(red)が優先されている のです.
この問題を回避するにはElement.style.fillプロパティを直接操作します.
発生する頻度はそれほど高くないと思われますが,注意すべき挙動です.Snap.svgの仕様とは言え,筆者としては統一感のない実装に思えてなりません.
ペイントサーバーの定義
ペイントサーバーは塗り潰しや線の中身を指します.通常は単色ですが,以下に示す機能を使えばより効果的なグラフィックとなります.
グラデーション
SVG標準のグラデーション定義は御世辞にも単純とは言えません.Snap.svgでは簡単なグラデーション記述を行うことで簡潔にグラデーションを定義できます.
Paper.gradient(gradientStr)
Paper.gradientメソッドはグラデーションによるペイントサーバーを生成します.本メソッドで生成したオブジェクトはfillプロパティやstrokeプロパティ等に直接指定できます.本メソッドで生成したguradientオブジェクトはPaperオブジェクトに対応するsvg要素に格納されます.
グラデーションオブジェクトの再取得
グラデーションを施したfill/strokeプロパティの内容をattrメソッドで取得するとurl(#[id])
形式の文字列が返されます.そのため,グラデーションの色を変えたい場合などで再度gradientオブジェクトが必要な場合は次のようにします.
グラデーション記述の書式
グラデーションは次のように指定します.
線形グラデーション(objectBoundingBox)
接頭辞を「l
」とします.
グラデーション対象の図形のbounding boxを[0,0]×[1,1]の矩形範囲に見立ててグラデーション方向を指定します.
色の間には-
を挿入します.
ストップオフセット値は色の後に:[offset値]
と指定します.
例)l(0,0,1,0)black-white:50-black
線形グラデーション(userSpaceOnUse)
接頭辞を「L
」とします.
グラデーション対象の図形が描かれている座標系を基準にグラデーション方向を指定します.
色の設定は上に同じです.
例)L(100,0,200,0)red-blue:25-red:75-blue
円形グラデーション(objectBoundingBox)
接頭辞を「r
」とします.
グラデーション対象の図形のbounding boxを[0,0]×[1,1]の矩形範囲に見立ててグラデーションの中心と半径を指定します.
色の設定は上に同じです.
例)r(0.25,0.5,0.5)green-yellow
円形グラデーション(userSpaceOnUse)
接頭辞を「R
」とします.
グラデーション対象の図形が描かれている座標系を基準にグラデーションの中心と半径を指定します.
色の設定は上に同じです.
例)R(200,300,200)white-yellow:50-red:51-orange
Snap.svgのドキュメントでは%を指定せよとありますが,おそらく間違いでしょう.
グラデーション内容へのアクセス
Paper.gradientメソッドで生成したElementオブジェクトにはグラデーション内容を操作するための機能が定められています.
Element.stops()
Element.stopsメソッドはgradientオブジェクト内部のstopオブジェクトのSetを取得します.
高度なグラデーション定義
グラデーションを反復する場合はgradientオブジェクトにspreadMethodプロパティを指定します.
パターン
グラデーションと同様にペイントサーバーとして利用可能なものがこのパターンです.タイルパターンを指定することで塗り潰しに自作のグラフィックを利用可能となります.
Element.toPattern(x,y,width,height)
Element.toPatternメソッドはパターン塗り潰しのためのpatternオブジェクトを生成します.circleメソッドやpathメソッド等により作られたElementオブジェクトに対し,tpPatternメソッドを実行するとグラフィックの内容がpattern要素内に移動され ,パターン画像として利用可能になります.
Element.pattern(x,y,width,height)
Element.patternメソッドは現在Element.toPatternメソッドのエイリアスです.Snap.svg0.3.0においては下位互換性のために定義されています.
複数の図形からパターンを定義したい場合は,Paper.ptrnメソッドを利用します.
Paper.ptrn([x,y,width,height[,vbx,vby,vbw,vbh]])
Paper.ptrnメソッドは直接patternオブジェクトを生成します.patternオブジェクトはPaperオブジェクトと同じように振る舞うため,直接図形要素を挿入できます.Paper.ptrnメソッドは引数の渡し方で動作が変化します.
引数未指定の場合
引数を何も指定しなかった場合,空のpatternオブジェクトを生成します.この場合,patternUnits属性にuserSpaceOnUseが設定され,Element.toPatternメソッドとほぼ同じように動作します.従って先程の例はPaper.ptrnメソッドを使って次のように書き換えることが出来ます.
x,y,width,heightを指定した場合
x,y,width,heightはタイルパターンのサイズを指定します.これらは図形要素のバウンディングボックスを基準としており(patternUnits=objectBoundingBox),0〜1の範囲で指定します.
x,y,width,height,vbx,vby,vbw,vbhを指定した場合
vbx,vby,vbw,vbhはpattern要素におけるビューボックスに対応します.タイル図形はこのビューボックスの描画範囲から生成されます.
pattern要素によるパターン敷き詰めはSVGの中でも理解しにくいものの一つです.詳しくはSVG仕様 を参照して下さい.
ElementオブジェクトによるDOM操作
ここまで解説したメソッドのほとんどは何らかのElementオブジェクトを生成しています.このElementオブジェクトはSVGDOMのSVGElementを内包しており,DOMを操作する上で便利な機能を多数提供しています.
※ここでのElementオブジェクトはSnap.svg独自のもので,XMLDOMにおけるElementオブジェクトとは異なるものです.
ElementとSVGElementとの相互変換
予めSVGElementオブジェクトが判っていた場合は,Snapメソッドを施すことでElementオブジェクトでラップすることが出来ます.また,逆にElementオブジェクトから元となるSVGElementを取り出したい場合はElement.nodeプロパティを参照します.
Snap(svgelement)
SnapメソッドはSVGElementオブジェクトを元にElementオブジェクトを生成します.
補足)HTMLの要素をSnapメソッドの引数に渡した場合
Snapメソッドの内部では引数に渡された内容がSVGに由来するものかHTMLによるものかを判定していません.そのためHTMLの要素を渡した場合もその時点ではエラーとはならず処理が継続されます .しかし,Snap.svgはHTMLにおけるインラインSVGを操作の対象としており,HTMLへの操作を想定していません.そのためSnap.svgが提供しているAPIの内,一概にどれが動作する・動作しないを判断することは困難です(DOM構造に関わる部分は動作する可能性が高い).下に例を示しますが,やむを得ない場合を除き,このような使い方は避けたほうがよいでしょう.
これはHTMLのdiv要素です.idとして"someDiv"を指定しています.
なお,要件がjQuery等のライブラリを導入するまでもない些細なものであれば,その代用として利用する手もあります.その際は十分に動作検証を行なって下さい.
Element.node
Element.nodeプロパティにはElementオブジェクトが内包しているSVGElementオブジェクトが格納されています.やむを得ずSVGDOMオブジェクトのもつapiを直接操作したい場合や,他のライブラリと連携する場合に重宝するでしょう.
Snap.svgを使うからと言ってSVGDOMを直接触る事を良しとしないのは間違いです.SVGDOMそのものにも魅力的なapiはたくさん備わっています.例えばSVGTextContentElement.getComputedTextLengthメソッドでは文字列の描画幅を求めることが出来ます.このようにSnap.svgの世界だけでコードを記述するのは,SVGDOMが本来持っているパワフルさを捨てることと同じです.
なお,今後Snap.svgがバージョンアップするにつれて同等のメソッドが提供される可能性はあります.
Elementオブジェクトのキャッシュと一意性
Snapでは内部で生成したElementオブジェクトをキャッシュしており ,当該要素に対するElementオブジェクトが既に作られていた場合,そのオブジェクトを返します.この仕組みにより,後述するElement.dataメソッドによる付属データの一意性が保証されます.
Elementオブジェクトのキャッシュとその注意点
このElementオブジェクトがキャッシュされるという特徴は一見便利ですが,一旦生成したElementオブジェクトは廃棄することが困難ということ でもあります.
Snap.svgでは現状Elementオブジェクトの開放に関わる仕組みが不十分な ため,デスクトップツール等の長期間動作させるようなwebページを作成する場合は,繰り返しElementオブジェクトを生成・廃棄することでメモリリークを引き起こします .従って,無闇にElementオブジェクトを生成するのではなく,適宜中身を書き換えてオブジェクトを再利用するようにしましょう.
なお,一般的な生存期間の短いwebページにおいてはそれほど問題とはならないでしょう.
オブジェクトの識別
Elementオブジェクト単体ではそれが何者かを判断することが出来ません.ここではオブジェクトの識別を行うメソッドについて説明します.
Element.type
Element.typeプロパティにはそのElementオブジェクトが内包しているSVGElementの要素名が格納されています.
Element.id
Element.idプロパティにはSnap.svgにおけるElementオブジェクトの固有IDが格納されています.この値はElementオブジェクトが作成された当初は中身のSVGElementオブジェクトには描きこまれておらず,Element.useメソッド等が実行されて初めてdomに描きこまれます .但し,内包しているSVGElementオブジェクトに既にid値が存在していた場合はElement.idの内容は無視されます.従ってElement.idとdomにおけるid値とは一概に同じ値とはなりません .
このようにElement.idプロパティは無闇に用いると思わぬバグを引き起こす可能性があり,使う場面が限られます.出来る限りElement.node.id
を直接参照・変更するようにしましょう.
SVGElement.snap
SVGElement.snapにはSnap.svgが割り当てた固有の値が格納されています.Snap.svgはこの値を元にElementオブジェクトをキャッシュしています.従って,この値の存在有無を確認することでSVGElementオブジェクトがSnap.svgの管理下にあるかどうかを判断できます.
要素の生成
要素の生成にはPaper.elメソッドを使います.
Paper.el(tagName, attr)
Paper.elメソッドは指定した名称の要素を生成し,それをラップしたElementオブジェクトを返します.作られた要素はPaperオブジェクトが内包しているsvg要素に自動的に配置されます.専用のメソッドが存在しない要素をsvg要素に追加したい場合に利用します.
例えばこの例ではa要素を生成し,リンクを作っています.
次の要素はこのPaper.elメソッドを使って生成することとなるでしょう.
a defs desc metadata symbol title view
SVGソースコードから要素を生成したい場合はSnap.parseメソッドを使い,Fragmentオブジェクトを生成します.
要素の複製
要素の複製にはcloneメソッドを利用します.
Element.clone()
Element.cloneメソッドは元々の要素ツリーを複製し,直後に挿入します .つまり,元のグラフィックの真上 に複製されたグラフィックが重なります.そのため,呼び出し方によっては意図しない動作に見える事があります.
要素の検索
Elementオブジェクトの中身を検索するにはselect/selectAllメソッドを利用します.
Element.select(selector)
Element.selectメソッドは引数で与えられたセレクタ文字列を元に要素を検索します.その結果,最初に見つかった要素に対するElementオブジェクトを返します.セレクタで見つかった全ての要素を取得するにはElement.selectAllメソッドを利用します.
XMLDOMのdocument.querySelectorメソッドに相当する機能です.セレクタ文字列の詳しい表記法についてはこちら を参考としてください.
webkit系のブラウザではこのセレクタによる要素の検索にバグがあります.インラインSVGにおいてキャメルケースの要素(例えばfeOffset等)は正しく選択することが出来ません.
Element.selectAll(selector)
Element.selectAllメソッドは引数で与えられたセレクタ文字列を元に要素を検索し,その結果をSetオブジェクトとして返します.
XMLDOMのdocument.querySelectorAllメソッドに相当する機能です.
要素の挿入
DOMツリーに要素を挿入する際に利用可能なメソッドを示します.SVGにおいては何れもグラフィックの描画順を決定する上で役に立つものばかりです.メソッドチェーンを円滑に行うためにメソッドを実行した主体のElementオブジェクトが返されます .
Element.append(element)
Element.appendメソッドは引数に与えたオブジェクトを子要素リストの末尾に追加します.
SVGではその構造上,図形を(そのレイヤの)最も上の位置に移動する 操作に相当します.
Element.add(element)
Element.addメソッドはElement.appendメソッドのエイリアスです.
Element.appendTo(element)
Element.appendToメソッドは引数に与えたオブジェクトの子要素リストの末尾にElementを追加します.
Element.prepend(element)
Element.prependメソッドは引数に与えたオブジェクトを子要素リストの先頭に追加します.
SVGではその構造上,図形を(そのレイヤの)最も下の位置に移動する 操作に相当します.
Element.prependTo(element)
Elemnt.prependToメソッドは引数に与えたオブジェクトの子要素リストの先頭にElementを追加します.
Element.before(element)
Element.beforeメソッドは引数に与えたオブジェクトをこの要素の前に挿入します.
SVGにおいてこの操作は元の図形の直下に図形を移動する ことに相当します.
Element.insertBefore(element)
Element.insertBeforeメソッドは引数に与えたオブジェクトの前にElementを挿入します.
Element.after(element)
Element.afterメソッドは引数に与えたオブジェクトをこの要素の後に挿入します.
SVGにおいてこの操作は元の図形の真上に図形を移動する ことに相当します.
Element.insertAfter(element)
Element.insertAfterメソッドは引数に与えたオブジェクトの前にElementを挿入します.
メソッドの分類
これらのメソッドを動作に応じて分類すると次のようになります.
メソッドの分類
対象を移動させる 自身が移動する
末尾に追加 append/add appendTo
先頭に追加 prepend prependTo
前に追加 before insertBofore
後に追加 after insertAfter
要素の削除
要素を削除するにはremoveメソッドを使います.
Element.remove
Element.removeメソッドはsvg要素から対応する要素を取り除きます.その結果,見た目上グラフィックから図形が削除されたように見えます.
なお,Element.removeメソッドを実行したとしてもElementオブジェクトが何らかの変数に格納されている限り再利用できます .そのためこのメソッドは本来の目的の他に,Paperオブジェクト(svg要素)をDOMから切り離す目的で用いられます.
Element.removeはElementオブジェクトの開放を意味しておらず,Elementオブジェクトのキャッシュはそのまま残り続けます .そのため,誤った用い方をすることでメモリリークを引き起こします .
ツリー構造に関わるメソッド・プロパティ
要素関係についての機能はそれほど多くありません.
Element.parent()
Element.parentメソッドは現在の要素に対する親要素に相当するElementオブジェクトを返します.
Element.paper
Element.paperプロパティには現在の要素が属するPaperオブジェクトが設定されています.Element.node.ownerSVGElementに相当します.
topレベルsvg要素に相当するPaperオブジェクトについては自分自身が設定されます.
それ以外の操作
上記以外の要素関係を得るにはElement.nodeプロパティとDOM標準のプロパティを利用します.Snapメソッドと組み合わせると良いでしょう.
childNodes 子要素のリストを取得します.
nextElementSibling 現在の要素の次を取得します.
previousElementSibling 現在の要素の前を取得します.
firstElementChild 現在の要素の先頭の子要素を取得します.
lastElementChild 現在の要素の末尾の子要素を取得します.
SVGコード変換とFragmentオブジェクト
Snap.svgではSVGソースコードとオブジェクト間の変換処理を簡単に行えます.ここではそれに関わるメソッド,及びその際に生成されるFragmentオブジェクトについて解説します.
SVGコードのパーシング
スクリプトからSVGを扱う場合,SVGのソースコードとSVGDOMとの相互変換がスムーズに行えると便利です.Snap.svgではこの用途のためのapiを提供しています.
Snap.parse(svgsource)
Snap.parseメソッドはSVGコードをElementオブジェクトのツリー構造オブジェクトとして取得します.このオブジェクトをFragmentオブジェクト と呼びます.このオブジェクトはElementオブジェクトではありませんが,そのままappendメソッド等に渡せるなど,ほぼElementオブジェクトと同様に振る舞います .
下の例ではcircleとrect要素を表すSVGコード文字列からFragmentオブジェクトを生成し,それをPaperオブジェクトに挿入しています.
二つの図形要素が一度にSVG画像内に挿入されていることがわかります.
筆者の環境では本メソッドが正しく動作しませんでした(おそらくドキュメント形式としてXHTMLを利用しているため).そのため,次のようなパッチを充てています.Snap.parseメソッドにおいて,次の1行
" + svg + "";]]>
を,このように修正しました.名前空間指定を追加しただけです.
" + svg + "";]]>
Fragmentオブジェクト
FragmentオブジェクトはSVGグラフィックの一部分を表すオブジェクトで,これはDOMにおけるDocumentFragmentオブジェクトに相当するものです.
SetオブジェクトとFragmentオブジェクトとg要素
一見するとこれら3つのオブジェクトはいずれもSVG要素の「集まり」と解釈できますが,厳密に役割が異なります.
Setオブジェクトは要素の集合を表します.
従ってSet内部を見ただけでは要素間の関係は判らず,単に数え上げることが出来るだけ です.また,Setオブジェクトに格納された要素は元々の位置に存在し続けます .
SetオブジェクトはElementオブジェクトの配列と見なせます.
FragmentオブジェクトはSVGグラフィックの一部を表します.
一方のFragment内部の要素については,中身に於いて明確な親子関係・兄弟関係が存在しており,Fragmentオブジェクトを頂点としたツリー構造をしています.また,Fragmentオブジェクトに格納された要素は元の位置から切り離されます .
FragmentオブジェクトはElementオブジェクトと同等に扱うことが可能で,言わば実体を持たない仮想的なElementオブジェクトである と考えると判りやすいです.
g要素はSVGグラフィックに対する構造を表します.
Paper.gメソッドで得られる(g要素)Elementオブジェクトはグラフィックの構造を表すもので,グラフィックの描画順や部品化に密接に関わっています.なお,DOMから切り離されたg要素は使い勝手上Fragmentオブジェクトとほぼ変わりませんが,要素としての実体をもつ点で異なります .
以上のことからSetオブジェクトは主に繰り返し処理のために利用され,FragmentオブジェクトはDOMツリーの変更を一度で済ますために利用します.また,g要素は図形要素を組み合わせることに何らかの目的がある場合に利用し,そこまでの必要が無い場合はFragmentオブジェクトで十分です.
一般にdocument.body/documentElementを頂点とするDOMツリーに対する操作は,(処理速度上)少ないほうが良いとされています.従って,(グラデーションやフィルター等により)複数の要素を挿入する必要がある場合,一旦Fragmentオブジェクトにてツリー構造を作っておいてから,最後にDOMツリーに追加することでDOMツリーへの変更を一度で済ませられます.
FragmentオブジェクトはSnap.parseメソッドの他にも次のメソッドを実行することで得られます.
Snap.fragment(source1,source2,...)
Snap.fragmentメソッドは任意個数のElementオブジェクト,もしくはSVGコードをFragmentオブジェクトとして取得します.
空のFragmentオブジェクトを生成する場合は引数に""
(空文字列)を渡します.
Fragment.node
Fragment.nodeプロパティには内部で管理しているDocumentFragmentオブジェクトが設定されています.Snap.svgには現状Fragmentオブジェクトに要素を追加する仕組みが存在しません.そのためnodeプロパティに対して直接SVGElementオブジェクトを追加します.
FragmentオブジェクトはElementオブジェクトではありませんが,内容を検索することが出来ます.
Fragment.select(selector)
Fragment.selectAll(selector)
Fragment.selectメソッド及びFragment.selectAllメソッドは指定したセレクタ文字列を元に内容を検索した結果を返します.
下の例ではFragmentオブジェクトの中身を検索した後にスタイルを追加しています.
SVGコードの編集
Snap.parseメソッドを呼び出す際,次のメソッドを利用するとテンプレートとなるSVGコードに任意の値を挿入する事ができます.
Snap.format(token,array/json)
Snap.formatメソッドは文字列を整形します.tokenには元となるテンプレート文字列を,array/jsonには挿入したいデータを指定します.
例えば日付文字列を挿入する場合は次のようにします.
配列を使った文字の置き換え
テンプレート文字列に{[配列インデックス]}
と記述しておくことで,メソッド実行時に配列の内容で置き換えます.
配列がネストしていた場合は{[配列インデックス].[配列インデックス]}
と指定することが出来ます.
jsonを使った文字の置き換え
テンプレート文字列に{[キー文字列]}
と記述しておくことで,メソッド実行時にjson内部を検索して値を置き換えます.オブジェクトがネストしている場合は{[親キー].[子キー]}
もしくは{[親キー]['[子キー]']}
と指定します.
複数のjsonを与えて逐次置き換えすることも可能です.
Snap.parse/Snap.fragmentメソッドとの連携
Snap.formatメソッドはSnap.parseメソッドと連携することで強力な要素生成機構となります.
SVGコードの取得
逆にsvg要素からSVGコードを取得することも出来ます.
Element.toString()
Paper.toString()
Element.toString/Paper.toStringメソッドは現在の要素構造を文字列として取得します.
Element.innerSVG()
Element.innerSVGメソッドは子要素の内容を文字列として取得します.
HTMLのinnerHTMLプロパティに似ていますが,値の設定は出来ません.
Element.outerSVG()
Element.outerSVGメソッドはElement.toStringメソッドのエイリアスです.
Snap.svgでの描画内容をファイル出力する
この機能を用いてSnap.svgで描いたグラフィックを外部ファイルとして出力できます.
Paper.toStringメソッドを実行し,基本となるSVGのソースコードを取得します.
既存の名前空間記述を削除し,新たに追加します(理由は後述します).
encodeURIComponentメソッドを使ってURL形式に変換し,先頭にURIデータスキーム形式を表す文字列data:image/svg+xml;charset=utf-8,
を追加します.
この文字列をa要素のhref属性やwindow.openメソッドの引数として渡すことで,ブラウザのファイル保存機能を使ってSVG文書として出力することが可能になります.
考慮すべき点は次の通りです.
色などの指定をstyle要素で行っていた場合,ファイル出力時にスタイル抜けが発生します.
HTML文書全体として成り立っていたものから一部分のみを切り出すため,このような問題が発生します.
複数のSVGグラフィックが存在していた場合,グラデーションやパターン等のdefs要素を参照している部分が壊れる場合があります.
上記と同じ原理で発生します.生成した要素が意図した場所に配置されないことによる問題です.
必ずしもSVGの名前空間指定が出力されるわけではありません.
この場合,svg要素の意味合いをブラウザが解釈できず,出力結果が単なるXMLファイルとなってしまいます.また,internet explorer環境では名前空間の指定が複数出力されてしまい,SVG文書としての整合性が壊れる場合があります.そのため上記のような対策が必要となります.
Setオブジェクトによる配列操作
SetオブジェクトはSnap.svgにおける要素の集まりを表すオブジェクトです.以下その特徴について示します.
Setオブジェクトの基本的特徴
SetオブジェクトはElementオブジェクトの集まりを表します.インデクサによるアクセスとlengthプロパティが利用可能なため配列的に操作することが出来る他,部分的にElement要素と同等のメソッドを実装しています.そのためSetオブジェクトを用いると単一の要素と要素セットとを意識せずに済みます.
Setオブジェクトが提供するElementオブジェクト互換のメソッド
remove
attr
insertAfter
getBBox
clone
これ以外のメソッドについては下に示すforEachメソッドと組み合わせて利用します.
Setオブジェクトが提供する配列様アクセッサメソッド/プロパティ
length
push
pop
forEach
clear
splice
exclude
なお,forEachメソッドについてはbreak機構が追加されています.
Setオブジェクトの生成
Setオブジェクトを生成するにはSnap.setメソッドを実行します.
Snap.set(element1,element2,...)
Snap.setメソッドは引数として与えたElementを格納したSetオブジェクトを生成します.
Element互換のメソッド
下記に示すメソッドを実行すると,中のElementの要素全てに対して同名のメソッドを実行します.
Set.remove()
Set.removeメソッドは管理しているElementオブジェクト全てを各々の親要素から削除し,かつSetオブジェクトの中身をクリアします.
Set.attr({key1:value1,key2:value2,...})
Set.attrメソッドは指定したプロパティを管理しているElementオブジェクトの全てに適用します.
Set.insertAfter(element)
Set.insertAfterメソッドは指定した要素の後ろにElementオブジェクトを挿入します.
Set.getBBox()
Set.getBBoxメソッドは全てのElementオブジェクトを囲むバウンディングボックスを取得します.
Set.clone()
Set.cloneメソッドはSetオブジェクトのコピー(ディープコピー)を取得します.
Set.animate(attrs,ms,easing,callback)
Set.animateメソッドは複数の要素にまたがるアニメーションを実行します.詳しくはアニメーションの項で解説します.
Set.attrメソッドとSet.bindメソッド
Set.attrメソッドに依る値の設定はSet.bindメソッドと組み合わせることで処理の中身を変更することが出来ます.
Set.bind(attr, element, e_attr/attr, function)
Set.bindメソッドではSet.attrメソッドによる処理を上書きします.通常Set.attrメソッドはSetオブジェクト管理下の要素に対して属性値の設定を行いますが,予めSet.bindメソッドを呼び出しておくことで任意の処理を呼び出すことが出来ます.
attr,element,e_attrを指定した場合
上記引数を指定した場合,Set.attrメソッドに於いてattr
属性を設定しようとした際に,element
オブジェクトのe_attr
属性が書き換えられます.e_attrが省略されていた場合はattrと同じものとして扱います.下の例では仮想の「gOpacity
」属性が指定された場合にg要素の不透明度が書き換えられるようにしています.
attr,functionを指定した場合
上記引数を指定した場合,Set.attrメソッドにおいてattr
属性を設定しようとした際に,function
関数が呼び出されます.その引数としてはSet.attrメソッドで渡したものが設定されます.
なお筆者はこのメソッドの効果的な使い方を残念ながらイメージ出来ていません.
配列様アクセッサ
Set要素は配列のように操作することが出来ます.
Set.length
Set.lengthプロパティにはSetが格納しているElementの数が格納されています.このプロパティを使うとfor構文を使った基本の数え上げ処理を行えます.
Set.push(element1,element2,...)
Set.pushメソッドでは任意個数のElementオブジェクトを既存の内容に追加します.
Set.pop()
Set.popメソッドではSetオブジェクトが管理する末尾のElementオブジェクトを返し,自身のサイズを縮小します.
Set.forEach(callback, thisArg)
Set.forEachメソッドではSetオブジェクトが管理する要素それぞれにcallback関数で定義された処理を施します.thisArgが指定されていた場合,callback内部におけるthis変数がthisArgを参照するようになります.
callback関数は次の書式で定義します.
elemには現在のElementオブジェクトが,iには現在のインデックス(0〜Set.length-1)が渡されます.ループ処理の途中で処理を中断(break)したい場合は,false
を返します.
Array.prototype.forEachメソッドではbreak処理が行えません.余談ですが,Array.prototype.everyメソッドとの中間的な動作となります.
Set.clear()
Set.clearメソッドは管理しているElementを全て開放し,中身を空にします.Set.removeメソッドと異なり,DOMからElementを削除することはありません.
Set.splice(index, count, insertion)
Set.spliceメソッドは管理しているElementのリストから指定したindexの位置からcountの個数削除し,新たなSetオブジェクトを生成します.また(もしあれば)insertionに指定したElement要素(もしくはそのリスト)で置き換えます.
下の例では左から右に円を5つ描いたのち,2番めと3番目の要素を取り出して赤く色を塗り,残りに青く色を塗っています.
下の例では左から右に円を5つ描いた後,2番目から4番目の要素を削除し,新たに二つの矩形をSetに挿入しています.その後,図形に赤・青・緑・黄の順に色を塗っています.
Set.exclude(element)
Set.excludeメソッドはSetの中から指定したElementオブジェクトの削除を試みます.削除に成功するとtrueが,そもそも要素が存在しなかった場合はfalseが返ります.
下の例では左から右に円を5つ描いた後,2つ目の要素を除いて赤色で塗りつぶしています.
Setオブジェクトから配列を生成する
配列的な操作を行うためのapiが提供されているものの,厳密にはArrayオブジェクとは異なります.従って,より配列的な操作が必要な場合はArray.prototype.sliceメソッドを利用し,Setオブジェクトを実際の配列に変換してしまうと良いでしょう.
下の例では左から右に円を5つ描いた後,circle要素を検索し,順序を反転しています.
配列で利用可能なメソッドの例を示します.目的に応じてSetオブジェクトと使い分けると良いでしょう.
pop
push
reverse
shift
sort
splice
unshift
concat
slice
Set.items
Set.itemsプロパティにはSetオブジェクトが管理しているElementオブジェクトの配列が格納されています.このプロパティはSnap.svgが内部管理しているものですから,使わないで下さい.
パスの編集に関わるユーティリティー機能
SVGDOMにおけるパスの編集機能はお世辞にも使いやすいとは言えません.Snap.svgではこの機能を大幅に強化しているため,SVGによる表現力が劇的に向上します.
Snap.pathオブジェクトが提供する機能
Snap.pathオブジェクトはSVGにおけるパス図形に対して様々なユーティリティーを提供します.以下にその内容を列挙します.
Snap.pathオブジェクトの内容
メソッド名 機能概要 Element
bezierBBox 3次ベジェ曲線に対するバウンディングボックスを取得します.
findDotsAtSegment 3次ベジェ曲線をtの位置で分割します.
getBBox パス文字列に対するバウンディングボックスを取得します.
getPointAtLength パスの道のりに対する座標を求めます. ○
getSubpath 部分パス文字列を取得します. ○
getTotalLength パスの全長を求めます. ○
intersection パスの交点を求めます.
isBBoxIntersect バウンディングボックスが共通部分をもつかを判定します.
isPointInside 閉じたパス図形の範囲内に座標が存在するかを判定します.
isPointInsideBBox バウンディングボックス内に座標が存在するかを判定します.
map パス文字列に行列による変形を施します.
toAbsolute パス文字列を絶対座標表記に変換します.
toCubic パスの内容を3次ベジェ曲線に変換します.
toRelative パス文字列を相対座標表記に変換します.
get 図形要素から同等のパス文字列を生成します.※非公開
バウンディングボックスに関わるもの
ここではバウンディングボックスに関わるメソッドについて説明します.
Snap.path.getBBox(pathString)
Snap.path.getBBoxメソッドは指定したパス文字列に対するバウンディングボックスを返します.
同様のメソッドはSVGDOMにも存在します.しかしSVGDOMのgetBBoxメソッドは対象となる要素が画面に表示されている前提で動作するため,要素がDOMツリーから切り離されていた場合やdisplay:noneにより非表示とされていた場合にエラーを発生します.一方のSnap.path.getBBoxメソッドではパス文字列だけでバウンディングボックスが得られる点で有益です.
Snap.path.bezierBBox(bezierpoints)
Snap.path.bezierBBoxメソッドは与えられたベジェ曲線を構成する座標データからバウンディングボックスを返します.
3次ベジェ曲線を定義するには始点・制御点1・制御点2・終点の4座標が必要となるため,このx座標,y座標を可変長引数もしくは配列として渡します.例えば,下の3つは同じベジェ曲線(のセグメント)を表します.
//ベジェ曲線のパス文字列表現
"M p1x,p1y C c1x,c1y c2x,c2y p2x,p2y"
//ベジェ曲線の可変長引数表現
p1x,p1y,c1x,c1y,c2x,c2y,p2x,p2y
//ベジェ曲線の配列表現
[p1x,p1y,c1x,c1y,c2x,c2y,p2x,p2y]
Snap.path.isBBoxIntersect(bbox1,bbox2)
Snap.path.isBBoxIntersectメソッドは二つのバウンディングボックスが共通部分をもつかを判定します.
パス上の座標に関わるもの
ここではパス形状と座標との関係を取り扱うものについて説明します.
Snap.getPointAtLength(path,length)
Snap.path.getPointAtLengthメソッドはパスに対して指定された道のり位置の座標を算出します.
得られるpInfoオブジェクトの内容は次の通りです.
x,y
指定位置の座標
alpha
指定位置における接線の角度
alpha値を用いるとパスに沿って図形を配置すると言った事が可能になります.
Snap.path.findDotsAtSegment(params)
Snap.path.findDotsAtSegmentメソッドは3次ベジェ曲線についてパラメータ位置tの部分でパスを分割します.
得られるpInfoオブジェクトの内容は次の通りです.
x,y
分割位置の座標
m.x,m.y
分割した前のパスの制御点2座標
n.x,n.y
分割した後のパスの制御点1座標
start.x,start.y
分割した前のパスの制御点1座標
end.x,end.y
分割した後のパスの制御点2座標
alpha
分割位置における接線の角度
実際の動作例を示します.
Snap.path.intersection(path1,path2)
Snap.path.intersectionメソッドは二つのパスの交点の位置を算出します.一般にパスの交点は複数存在し得るため,本メソッドは配列データを返します.
各交点毎の情報オブジェクトの内容は次のとおりです.
x,y
交点の座標
t1
パス1に対するt値
t2
パス2に対するt値
segment1
パス1に対する交点が存在するパス切片のID
setment2
パス2に対する交点が存在するパス切片のID
bez1
パス1におけるパス切片のベジェ曲線表現(配列)
bez2
パス2におけるパス切片のベジェ曲線表現(配列)
交点を表示するサンプルを示します.
パスの長さに関わるもの
ここではパスに対する長さに関わるものについて解説します.
Snap.path.getTotalLength(pathString)
Snap.path.getTotalLengthメソッドはパスに対する長さを取得します.
例えば破線の長さを全体の長さから求めればバランスの良い破線が得られるでしょう.
Snap.path.getSubpath(pathString,from,to)
Snap.path.getSubpathメソッドは与えられたパスに対する部分パスを取得します.
パスの一部の色を変化させたいと言った場合に重宝するはずです.
座標の位置関係に関わるもの
ここではパス図形と座標との相関に関わるメソッドについて説明します.
Snap.path.isPointInside(pathString,x,y)
Snap.path.isPointInsideメソッドは指定した座標がパス図形が定める範囲に含まれているかを判定します.
但し,複雑な図形に対しては正しい判定結果とならないケースもあるようです.
Snap.path.isPointInsideBBox(bbox, x, y)
Snap.path.isPointInsideBBoxメソッドは与えられた矩形に対して座標が含まれているかを判定します.
bboxに与えるオブジェクトはElement.getBBoxメソッドから得たものでなくとも,x,y,width,heightを含んでいれば動作します.
Elementオブジェクトが実装しているメソッド
上記の中にはElementオブジェクトから直接呼び出せるものがあります.何れも内部に保持している要素がpath要素でなかった場合は無効です.
Element.getPointAtLength(length)
Element.getPointAtLengthメソッドは指定された道のり位置の座標を算出します.Snap.path.getPointAtLengthと同等の結果を返します.
Element.getSubpath(from,to)
Element.getSubpathメソッドは指定した道のり位置に対する部分パス文字列を返します.Snap.path.getSubpathメソッドと同等の結果を返します.
Element.getTotalLength()
Element.getTotalLengthメソッドはパスの全長を返します.Snap.path.getTotalLengthメソッドと同等の結果を返します.
PathSegmentsオブジェクトとパスの変換
ここではパスのセグメント分解,及び各種パスの変換処理について解説します.
Snap.parsePathString(pathstring)
Snap.parsePathStringメソッドは与えられたパス文字列をパスセグメント要素に分解します.形式としては配列の配列となります.
例えばM10,10L180,180Q120z
を本メソッドで分解すると次のようになります.
[
["M", "10", "10"],
["L", "180", "180"],
["h", "15" ],
["z" ]
]
Snap.svgではこの「配列の配列」に対して独自機能を追加しているため,本ページでは便宜上PathSegmentsオブジェクト呼称します .
]]>
PathSegmentsオブジェクトはpathデータを表します.配列を拡張しているため,Arrayオブジェクトが提供している各種配列操作を利用することが出来,パスをセグメント(切片)毎に操作することができます.
PathSegmentsオブジェクトと各種メソッドとの関わり
PathSegmentsオブジェクトは,そのままElementオブジェクトのpathプロパティに設定することが出来ます .
なお,分解した配列から元のパス文字列を復元するにはtoStringメソッドを使って配列データを連結します.下の例では,元々のパス文字列,セグメント配列に分解したパスデータ,復元したパス文字列から3つのpath要素を生成しています.何れも同じ形となっていることが判るでしょう.
PathSegments.toString()
PathSegments.toStringメソッドはパス文字列を返します.
以下はこのPathSegmentsオブジェクトを返すメソッドです.
Snap.path.map(pathData,matrix)
Snap.path.mapメソッドは与えられたパスデータに直接アフィン変換を適用して,変形後のパスデータを取得します.
座標変換による図形の変形には通常transform属性を用いますが,このメソッドは直接パス文字列を変形します.stroke幅を変えずに図形を変形したいと言った場合に用いると良いでしょう.
パス文字列を変形する場合,ネックになるのがA操作による楕円弧セグメントです.変形により傾きが変化するため,単純な操作だけでは上手く行かないのです.
Snap.path.toRelative(pathData)
Snap.path.toRelativeメソッドは図形の内容を変化させずにパスデータを全て相対位置指定コマンドに置き換えます.
Snap.path.toAbsolute(pathData)
Snap.path.toAbsoluteメソッドは図形の内容を変化させずにパスデータを全て絶対位置指定コマンドに置き換えます.
Snap.path.toCubic(pathData)
Snap.path.toCubicメソッドは図形の内容を変化させずにパスデータを全て3次ベジェ曲線コマンドに置き換えます.
理論上Aコマンドによる楕円弧はベジェ曲線に変換することができないので,Cコマンドで近似しています.そのため,厳密には異なるパスになってしまいますが,その誤差を目視することは不可能でしょう.
パス制御機能の応用
セクションの締めくくりとして,Snap.pathの応用例について示します.
カーソル位置でパスを分割する
この例では,与えられたパス図形にカーソルを近づけると,その位置でパスを分割しています.
transformとMatrix
SVGDOMが提供するtransfromに関わるapiは,その構造の複雑さから非常に扱いにくいものでした.Snap.svgではこの内容をより直感的に扱う事を可能としています.
transformプロパティによる図形の変形
Snap.svgにおいてもtransformによる変形をサポートしています.
Element.transform(transformString/matrix)
Element.transformメソッドは与えたtransform文字列を元に図形の変形を行います.また,引数にMatrixオブジェクトを渡すことも出来ます。
下の例では,元々の黒い四角形をtransformメソッドにより赤い四角形に変形しています.
attrメソッドでのtransformプロパティの指定
transformプロパティはattrメソッドから指定することもできます.但し,transformプロパティの中身は自動的にSnap.svgでのtransform記述形式に変換されます.
Snap.svgがサポートしているtransform関数
Snap.svgではSVG標準で提供しているtransform関数を拡張している他,短縮記法をサポートしています.
transform関数の一覧
変換内容 SVG記法‡ Snap.js記法 Matrixメソッド
行列変換 matrix(a,b,c,d,e,f) m[a],[b],[c],[d],[e],[f] /m(a,b,c,d,e,f) 例)m1,2,3,4,5,6 Matrix.add
平行移動 translate(x,y) t[x],[y] /t(x,y) 例)t10,20 Matrix.translate
拡大・縮小 scale(x,y,cx,cy) ※cx,cyはSnap.svgによる拡張 s[x],[y],[cx],[cy] /s(x,y,cx,cy)† 例)s1.2,1,100,100 Matrix.scale
回転 rotate(a,cx,cy) r[a],[cx],[cy] r(a,cx,cy)† 例)r30,100,100 Matrix.rotate
せん断 skewX(a) skewY(a) ※ ※
※Snap.svgには対応するものがありません.自動的にmatrixに変換されます.
†中心座標を省略した場合,SVG記法とSnap.js記法とで動作が異なります..
‡SVG記法を使う場合は,関数を列挙する際は間に空白[
]を挿入して下さい. ※カンマ「,
」は使えません.
例を示します.
SVG記法とSnap.svg記法の違い
scale操作とrotate操作においては基準点を省略した際の動作が異なります.
SVG記法…原点が与えられたものとして動作します.
Snap.svg記法…図形のバウンディングボックスの中心が与えられたものとして動作します.
例を示します.変換の基準点が異なるため,見た目に違いが現れています.
座標変換の行列表現
先ほどの座標変換は一般に3次の行列として表すことができます.この時,変換の重ねあわせが行列の掛け算に対応するなど,行列の演算が重要な役割を果たします.そこでSnap.svgではこの行列演算を司るMatrixオブジェクトが提供されています.
Snap.matrix(a,b,c,d,e,f)
Snap.matrixメソッドはアフィン行列オブジェクトを生成します.json形式,SVGMatrix等を渡すことも出来ます.引数を省略すると(1,0,0,1,0,0)(恒等変換)が与えられたものとして扱われます.MatrixオブジェクトをElement.transformメソッドに渡すことでその図形が描画される座標軸の変換を行います.
例を示します.本来黒い円として描画されるはずのグラフィックが,アフィン行列によって赤い円に変形されていることがわかります.
引数と行列との関係
ここで引数a〜fは行列で表すと,次のように対応します.
┌ ┐
│ a c e │
│ b d f │
│ 0 0 1 │
└ ┘
ここで元々の座標軸(X,Y)に対してこの行列が引き起こす変換先の座標軸(x,y)との関係を式で表すと次のようになります.
┌ ┐ ┌ ┐┌ ┐
│ X │ │ a c e ││ x │
│ Y │=│ b d f ││ y │
│ 1 │ │ 0 0 1 ││ 1 │
└ ┘ └ ┘└ ┘
つまり,変換先の座標軸上の座標を与えると,元々の座標軸での座標が求まることになります.
行列演算のためのメソッド群
Matrixオブジェクトには行列の演算を行うためのメソッドが提供されています.
行列演算メソッドとtransform属性との関係
Matrixオブジェクトのadd,translate,scale,rotateは何れも与えた行列を右から 掛け合わせます.これはtransform属性において,transform文字列の末尾にmatrix操作を追加 したことに相当します.例えば,次のようなtransform文字列が与えられたとします.
transform="func1 func2 func3"
ここでそれぞれのtransform関数に対応する行列を[func1]〜[func3]
として表すと,先ほどのtransform文字列は行列の掛け算に対応します.
┌ ┐┌ ┐┌ ┐
│func1││func2││func3│
└ ┘└ ┘└ ┘
例を示します.行列の計算結果を渡した場合とtransform文字列を直接指定した場合とで,全く同じ結果となっていることがわかります.
Matrix.add(a,b,c,d,e,f/matrix)
Matrix.addメソッドは引数として与えた行列を自身の右から掛け合わせます.
┌ ┐ ┌ ┐ ┌ ┐
│ │ │ │ │ a c e │
│ Result │=│ Current │x│ b d f │
│ │ │ │ │ 0 0 1 │
└ ┘ └ ┘ └ ┘
Matrix.translate(x,y)
Matrix.translateメソッドは平行移動操作行列を自身の右から掛け合わせます.
┌ ┐ ┌ ┐ ┌ ┐
│ │ │ │ │ 1 0 x │
│ Result │=│ Current │x│ 0 1 y │
│ │ │ │ │ 0 0 1 │
└ ┘ └ ┘ └ ┘
Matrix.scale(x,y,cx,cy)
Matrix.scaleメソッドは拡大縮小操作行列を自身の右から掛け合わせます.yを省略するとxと同じ値と解釈します(一様拡大).cx,cyは拡大する際の基準となる座標を指定します.
┌ ┐ ┌ ┐ ┌ ┐┌ ┐┌ ┐
│ │ │ │ │ 1 0 cx││ x 0 0 ││ 1 0 -cx│
│ Result │=│ Current │x│ 0 1 cy││ 0 y 0 ││ 0 1 -cy│
│ │ │ │ │ 0 0 1││ 0 0 1 ││ 0 0 1│
└ ┘ └ ┘ └ ┘└ ┘└ ┘
Matrix.rotate(a,x,y)
Matrix.rotateメソッドは回転行列を自身の右から掛け合わせます.x,yは回転の中心座標を指定します.
┌ ┐ ┌ ┐ ┌ ┐┌ ┐
│ │ │ │ │ 1 0 x ││ cos(a) -sin(a) -x│
│ Result │=│ Current │x│ 0 1 y ││ sin(a) cos(a) -y│
│ │ │ │ │ 0 0 1 ││ 0 0 1│
└ ┘ └ ┘ └ ┘└ ┘
Matrix.invert()
Matrix.invertメソッドは逆行列を生成します.尚,Matrixの状況によっては逆行列が存在しない場合もあります.
逆行列の役割
座標変換と行列との関係を表した式について,両辺に逆行列を左から掛けると次のようになります.
┌ ┐-1┌ ┐ ┌ ┐-1┌ ┐┌ ┐ ┌ ┐
│ a c e │ │ X │ │ a c e │ │ a c e ││ x │ │ x │
│ b d f │ │ Y │=│ b d f │ x│ b d f ││ y │=│ y │
│ 0 0 1 │ │ 1 │ │ 0 0 1 │ │ 0 0 1 ││ 1 │ │ 1 │
└ ┘ └ ┘ └ ┘ └ ┘└ ┘ └ ┘
この式から,逆行列によって変換元の座標を与えると変換先の座標が求まることがわかります.
逆行列の利用例
この関係はしばしばScreenCTMと組み合わせて用いられます.ScreenCTMはSVG座標をスクリーン座標に変換する行列ですから,この逆行列を算出することでカーソル位置等のスクリーン座標からSVG座標が求まります.
変換行列の分解
変換行列は一般に複数の原始行列(つまりtranslate, scale, skew/shear, rotateの4操作)の掛け算に分解できることが知られており,Snap.svgではこの処理をMatrix.splitメソッドとして提供しています.
Matrix.split()
Matrix.splitは元となる行列を原始操作の積に分解し,次の数値を格納した原始Matrix情報オブジェクトを返します.
dx,dy
平行移動成分
scalex,scaley
拡大・縮小成分
shear
せん断成分
rotate
回転成分
isSimple
原始行列かどうかの判定boolean値
本メソッドで得られた原始Matrix情報オブジェクトの内容は次のtransform文字列と同等 です.Snap.formatメソッドに渡すことで元のMatrixオブジェクトと同等のtransform文字列が得られます.なお,このtransform関数の記述順を交換すると正しい結果となりません .
translate({dx},{dy}) scale({scalex},{scaley}) matrix(1,{shear},0,1,0,0) rotate({rotate})
shear値はatan関数を用いるとskewY操作に変換できます.
例を示します.
なおこの操作は次のような用途に利用できますが,実際には余り使われないかも知れません.
鏡像成分の抽出
スケール値に負数が含まれている場合,そのグラフィックが反転していることになります.
グラフィックの一部固定化
スケール値や回転値の逆数を求め,固定化したい内容(ストローク幅,傾き,サイズ等)に変形を施しておきます.こうすると全体での変形と個別の変形とが打ち消し合ってグラフィック内容の一部を固定化することが出来ます.
行列の複製
行列の内容はMatrix.cloneメソッドを使うことで複製することが出来ます.
Matrix.clone()
Matrix.cloneメソッドは現在の行列オブジェクトを複製します.
座標に関わるメソッド
Matrixオブジェクトから座標の変換先を求めることが出来ます.
Matrix.x(x,y)/Matrix.y(x,y)
Matix.xメソッドは与えた座標に対して行列変換を行い,結果得られたx座標を返します.Matix.yメソッドは与えた座標に対して行列変換を行い,結果得られたy座標を返します.
例を示します.
Transform文字列の取得
Matrixオブジェクトからは2種類のTransform文字列が得られます.
Matrix.toTransformString()
Matrix.toTransformStringメソッドは行列の内容をSnap.svg形式のTransform文字列として取得します.
Matrix.toString()
Matrix.toStringメソッドは行列の内容をSVG形式のTransform文字列として取得します.
transform状況の取得
先ほど示したElement.transformメソッドを使うとより細かいtransform変形の内容を取得できます.
Element.transform()
Element.transformメソッドはその要素に現在設定されている様々なtransform状況を返します.このメソッドで得られる情報は次の通りです.
string
要素単体に設定されているtransform文字列
globalMatrix
SVG全体での変換行列
localMatrix
要素単体の変換行列
diffMatrix
要素が存在している座標系の変換行列
global
globalMatrixに対する文字列表現
local
localMatrixに対する文字列表現
toString()
stringを返します.
残念ながらSVGDOMのgetScreenCTMメソッドに相当する機能は無いようです.
例えば次のようなコードが存在したとします.
<==元々の階層(このsvg要素のScreenCTMをSとする)
<== Aの階層
<== ABの階層
<== ABCの階層
]]>
すると,transform状況の内容は次のようになります.(SVGDOMのgetCTM/getScreenCTMメソッドから返される値も合わせています)
このメソッドは図形の位置や大きさを制御する際に用います.次の例ではtransform変形が為されているcircle要素の中心を基準に円を描くサンプルです.
このように,図形に対してどのような変形が施されていたとしてもその座標を計算できます.どの行列を用いるかは図形をどの階層に挿入したいかによって変化します.例えば対象とする図形と同じ階層に配置するのであれば,localMatrix/stringを施せばよいでしょう.
その他のtransformに関わるユーティリティー
ここではその他のユーティリティー関数について示します.
Snap.parseTransformString(transformString)
Snap.parseTransformStringメソッドはSnap.svg形式のtransform文字列をtransform要素毎の配列に分解します.
このメソッドはSnap.svgが内部で利用しているもので,apiこそ公開されていますが,その使い途についてはいまいち思いつきません.transformの制御には極力Matrixオブジェクトを用いましょう.
gradientTransform,patternTransform属性への設定
Matrixオブジェクトはgradientオブジェクト,patternオブジェクトに対しても有効です.
gradientオブジェクトへの適用
gradientオブジェクトへはgradientTransform属性を用います.
patternオブジェクトへの適用
patternオブジェクトへはpatternTransform属性を用います.
色に関わるユーティリティー関数群
cssでは様々な形式の色指定が可能ですが,その相互変換が出来ませんでした.Snap.svgでは色に関わる様々なユーティリティー関数が定義されています.
なお現バージョンではアルファチャネルの取り扱いがメソッド毎に整理されておらず,非常に面倒なことになっています.
サポートしている色の書式
Snap.svgがサポートしている色の書式を示します.
記述可能な色の書式
形式 説明 例
color-name HTML色名 yellow
#000 色の16進(hex)表示 #eee
#000000 色の16進(hex)表示 #d0f0d0
rgb(x,x,x) RGB色空間(赤・緑・青)
rgb(128,128,255) rgb(50%,50%,100%)
rgba(x,x,x,x) RGB色空間+アルファチャネル
rgba(128,128,255,0.5) rgba(50%,50%,100%,50%)
hsb(x,x,x) HSB色空間(色相・彩度・明度)※
hsb(0.5,0.5,0.5) hsb(50%,50%,50%)
hsba(x,x,x,x) HSB色空間+アルファチャネル※
hsba(0.5,0.5,0.5,0.5) hsba(50%,50%,50%,50%)
hsl(x,x,x) HSL色空間(色相・彩度・輝度)
hsl(0.5,0.5,0.5) hsl(50%,50%,50%)
hsla(x,x,x,x) HSL色空間+アルファチャネル
hsla(0.5,0.5,0.5,0.5) hsla(50%,50%,50%,50%)
※Snap.svgの拡張です.
パーセント値/角度による値の指定
色の指定にパーセント値を混在することが出来ます.また,色相値には角度を指定することも出来ます .
色相環の作成例
以下に色相の角度と色の関係を示します.
例では複数のpath要素を使って色相環を表現していますが,このような用途にSVGを用いるのはお勧めできません.可能であればcanvas要素を使ってラスタ画像を生成するようにしたほうがよいでしょう.
色書式の相互変換
先ほどの色書式の内容について,相互変換するためのメソッドが多数収録されています.処理の内容によって分類してみましょう.
色情報オブジェクトを生成するもの
color…色情報の全てを生成するもの
getRGB…RGB情報のみを生成するもの
特定の形式間の変換を行うもの
hsb2rgb,hsl2rgb,rgb2hsb,rgb2hsl
hex形式専用…指定した内容をhex形式の文字列に変換する.アルファチャネル非対応.
rgb,hsb,hsl
変換関数の相関
変換先
hex rgb hsb hsl
変換元
color name
color/getRGB
hex
- color getRGB color getRGB color getRGB
rgb
rgb - rgb2hsb rgb2hsl
hsb
hsb hsb2rgb - color getRGB
hsl
hsl hsl2rgb color getRGB -
Snap.color(colorString)
Snap.colorメソッドは色文字列から色情報オブジェクトを生成します.
色情報オブジェクト
色情報オブジェクトの構造は次のとおりです.ほぼすべての内容を網羅します.
r
赤成分(0〜255)
g
緑成分(0〜255)
b
青成分(0〜255)
h
色相(0〜1)
s
彩度(0〜1)
v※
明度(0〜1)※b値は青に対応します.
l
輝度(0〜1)
opacity
不透明度/アルファチャネル(0[透明]〜1[不透明])
hex
hex形式文字列
error
変換の成否boolean値
toString()
rgb形式/rgba形式の色文字列を返します.
Snap.getRGB(colorString)
Snap.getRGBメソッドは色文字列を解析し,RGB色情報オブジェクトを返します.RGB色情報オブジェクトの構造は以下のとおりです.
r
赤成分(0〜255)
g
緑成分(0〜255)
b
青成分(0〜255)
opacity
不透明度/アルファチャネル(0[透明]〜1[不透明])
hex
hex形式文字列
error
変換の成否boolean値
toString()
rgb形式/rgba形式の色文字列を返します.
Snap.hsb2rgb(h,s,b,opacity)
Snap.hsb2rgbメソッドは色相(h),彩度(s),明度(b),不透明度を元にRGB色情報オブジェクトを生成します.
Snap.hsl2rgb(h,s,l,opacity)
Snap.hsl2rgbメソッドは色相(h),彩度(s),輝度(l),不透明度を元にRGB色情報オブジェクトを生成します.
Snap.rgb2hsb(h,s,l,opacity)
Snap.hsl2rgbメソッドは赤(r),緑(g),青(b),不透明度を元にHSL色情報オブジェクトを生成します.
HSB色情報オブジェクトの構造は次のとおりです.
h
色相(0〜1)
s
彩度(0〜1)
b
明度(0〜1)
toString()
hsb形式の色文字列を返します.
Snap.rgb2hsl(h,s,l,opacity)
Snap.hsl2rgbメソッドは赤(r),緑(g),青(b),不透明度を元にHSL色情報オブジェクトを生成します.
HSL色情報オブジェクトの構造は次のとおりです.
h
色相(0〜1)
s
彩度(0〜1)
l
輝度(0〜1)
toString()
hsl形式の色文字列を返します.
Snap.rgb(r,g,b)
Snap.rgbメソッドは赤(r),緑(g),青(b)を元に16進形式の色文字列を生成します.
Snap.hsb(h,s,b)
Snap.hsbメソッドは色相(h),彩度(s),明度(b)を元に16進形式の色文字列を生成します.
Snap.hsl(h,s,l)
Snap.hslメソッドは色相(h),彩度(s),輝度(l)を元に16進形式の色文字列を生成します.
色に関連するキーワードについて
SVGでは色に関わる特殊なキーワードが定められており,Snap.svgにおいてもこれらのキーワードを利用することが出来ますが,注意すべき点があります.
"none"はグラフィック描画を「行わない」事を表します.
fill属性やstroke属性にキーワード「none
」を指定した場合,SVGでは描画処理をスキップする 意味合いとなります.これは見た目上透明色を指定した場合と同じですが,クリック等のカーソルイベントの発生に明確な違いが発生します .通常noneを指定した部分では各種カーソルイベントが発生しません.なおイベントの発生範囲はpointer-eventsプロパティで制御することが出来ます.
Snap.svgでは"currentColor"値が正しく動作しません.
SVGではfillやstroke等の色を扱う属性に対して「currentColor
」を指定すると,その要素もしくは祖先を登った先のcolorプロパティでの色を参照する事ができます.しかしSnap.svgではこの値を正しく扱うことが出来ません.この問題を回避するにはnode.styleプロパティを直接操作して下さい.
Element.attrメソッドを使ってfill/stroke属性にHTML色を指定するとhex形式に変換されてしまいます.
従ってセレクタを用いた「red色で塗りつぶした要素を再取得する」と言った処理が出来ません.例を示します.
この問題は検索条件に用いるセレクタ文字列の内容をhex形式に書き換えるか,Element.node.setAttributeメソッドを使ってfill/stroke属性を直接書き換えることで対処します.
この現象はSnap.svgが色文字列の処理に内部でWindow.getComputedStyleメソッドを利用していることに起因しています.
データ操作に関わるユーティリティー関数群
データ型やデータの格納に関わるユーティリティー関数が定義されています.
データの保管
次のメソッドは要素に一時的に何らかのデータを保管しておきたい場合に利用します.なお,これはhtml5で採用されたdata属性とは関係ありません.
Element.data(key,value)
Element.dataメソッドは一時的なデータコンテナを提供します.引数にkey-valueを与えることでvalueを中に格納することが出来ます.
Element.data(key)
Element.dataメソッドにkey値のみ渡した場合は,内部のvalue値を取り出すことが出来ます。
ElementオブジェクトはSnap.svg内部にキャッシュされています.そのため一旦格納したデータは,再度Elementオブジェクトを取得した際にも正しく取り出せます.
Element.removeData(key)
Element.removeDataメソッドは指定したkey値に対応するデータを削除します.
データの変換
ここではその他のユーティリティー関数について示します.例えば角度を扱う場合は次のメソッドが便利です.
Snap.rad(deg)
Snap.radメソッドは角度をラジアンに変換します.−2π〜2πの値を返します .
Snap.deg(rad)
Snap.degメソッドはラジアンを角度に変換します.-360〜360の値を返します .
Snap.rad,Snap.degメソッドは返り値の範囲が固定されてしまうため,実際には扱いにくい機能になってしまっています.
Snap.angle(x1,y1,x2,y2,x3,y3)
Snap.angleメソッドは3点が為す角度を取得します.3点A(ax,ay),B(bx,by),C(cx,cy)が与えられたとします.
Snap.angle(ax,ay,bx,by)
2点が与えられた場合,ベクトルBAの傾斜角 を取得します.引数の内容と方向に注意して下さい.
Snap.angle(ax,ay,bx,by,cx,cy)
3点が与えられた場合,ベクトルCBを基準にCAが為す角度(∠BCA) が取得されます.
このように引数の順番が直感的ではなく,場合によってはMath.atan2メソッドを直接用いたほうが判りやすくなるかもしれません.
判定処理
Snap.svgではデータ型の判定をSnap.isメソッドで行っています.
Snap.is(value,type)
Snap.isメソッドはvalue値の型がtypeであるかどうかを判定します.判定可能な値は次の通りです.
null
finite
object
array
string
number
function
SVGElement
ですが,特にこだわりが無い限り使う必要は無いでしょう.
アニメーション
SVG標準のアニメーション機構(SMIL)は現状問題が多く,今日最も利用されているものはDOM操作によるアニメーションです.Snap.svgではこのDOMアニメーションをサポートする強力な仕組みを提供します.
ですが,現状ではアニメーション化出来ない属性も存在します.今後改善していくことでしょう.
SVGとアニメーション
一般にSVGがサポートするアニメーションには次の3つが存在します.
SMILによるアニメーション
SVGが標準的に備えているアニメーション機構.SVG要素にアニメーションを定義する要素を配置するもの.
CSSによるアニメーション
SVGがCSSをサポートすることにより副次的に導入されるアニメーション機構.SVGのスタイル(見た目)の部分をアニメーション化します.
SVGDOMによるアニメーション
webブラウザではjavascriptからSVGDOMを操作することでアニメーションを表現できます.
この内Snap.svgはSVGDOMによるアニメーションを強力にサポートします.
SMILによるアニメーションをSnap.svgから利用することは理論上可能ですがおすすめしません.というのもSMILの仕様自体曖昧かつ複雑なものであり,現状正しく実装しているブラウザが存在しないからです.それどころか仕様の未熟さに起因する潜在的なセキュリティリスクすら存在するため,IEのようにSMIL自体をサポートしないものもあります.この状況は今後も好転するとは思えません.
Snap.svgにおける基本のアニメーション
Snap.svgが提供しているアニメーションに関わるメソッドには次の4つがあります.
Element.animateメソッド
単一の要素をアニメーション化する場合に便利なメソッドです.手軽に実行できる代償として,一部の機能(一時停止・再開等)が使えません.
Set.animateメソッド
複数の要素にまたがるアニメーションを実行します.手軽に実行できる代償として,一部の機能(一時停止・再開等)が使えません.
Snap.animateメソッド
複数のグラフィック要素を同時にアニメーション化する場合に利用します.工夫次第でアニメーションのタイムライン制御も可能です.
minaメソッド
Snap.svgが内部で利用しているアニメーションライブラリを直接利用する方法です.高度にアニメーションを抽象化しているため応用範囲が広い反面,扱いにくいというデメリットもあります.
アニメーションを実行する場合,Elementオブジェクトのanimateメソッドを用います.
Element.animate(attr, dur, easing, callback)
Element.animateメソッドは対象要素の指定した属性の値を徐々に変化させることでアニメーションを表現します.attrにはアニメーション終了時の値をkey-value形式で指定します.durにはアニメーションの実行時間をミリ秒で指定します.easingにはイージング関数を指定します.callbackにはアニメーション終了時に呼び出される関数を渡します.
イベント処理等からアニメーションを実行する場合は,必ずアニメーション実行前にstopメソッドを実行しておきましょう.
アニメーション終了時に値を元に戻す
アニメーション終了時に値を元に戻す場合は,コールバック関数にその内容を記述します.
アニメーションを繰り返す
アニメーションを繰り返すのであれば,例えば次のようにアニメーション開始関数を定義し繰り返し実行されるようにします.
異なるアニメーションを同時実行する
また,異なるアニメーションを同時に実行することもできます.
アニメーションの逐次実行
異なるアニメーションを逐次的に実行する場合は,callback関数内に続きの処理を記述します.
なお,匿名関数による記述では処理を連ねる毎に関数のネストが深くなっていくため,可読性が損なわれます.
Element.stop()
Element.stopメソッドは対象のElementオブジェクトで実行されているアニメーションの全てを停止します.
下の例ではアニメーションが開始された1秒後にアニメーションを停止しています.
複数の要素にまたがるアニメーションを一括停止する
Element.animateメソッドで複数個の要素についてアニメーションを実行した場合,それらを一括して停止する方法はありません.そのような処理が必要な場合は個別に停止するようにします.
アニメーション対象の属性
Element.animateメソッドを使ってアニメーション化出来るプロパティには次のものがあります.
数値を扱うプロパティ(x,y,width,height,cx,cy...)
数値のリストを扱うプロパティ(points)
色を扱うプロパティ(fill/stroke)
path要素のd属性
transform/gradientTransform/patternTransform属性
上記以外のプロパティ(font-sizeプロパティ等)についても,うまくElement.equalメソッドを拡張することでアニメーション化させることができます.
イージング関数を指定する
イージング関数は経過時間とアニメーション変位割合とをマップする関数を指し,イージング関数を与えることにより,表情豊かなアニメーションになります.Snap.svgではイージング関数の指定にファンクションオブジェクトを直接指定します .そのため,他のライブラリにおけるキーワードを与える方法よりもより柔軟なイージング処理を指定することが可能となっています.下の例ではイージング関数としてmina.easeinを指定しています.
minaオブジェクトのイージング関数
Snap.svgではよく使うイージング関数としてminaオブジェクトに7つのイージング関数が定義されています.以下にその動作例を示します.
イージング関数の変化量
イージング関数をグラフにすると次のようになります.横軸が時間経過(time),縦軸を変位割合(percentage)としています.
t=0
t=1
p=0
p=1
イージング関数の自作
minaオブジェクトで定義されているものの他にもイージング関数は自作できます.引数として区間[0〜1]の数値(経過時間)を受け,進捗値(通常0が初期位置,1が終了位置)を返すように関数を定義すると,イージング関数として機能します.下の例では経過時間を自乗する関数を使って放物運動を表現しています.
なおこのテクニックはアニメーションを設計する上で非常に重要です.工夫次第ではアニメーションの繰り返しや移動方向の制御に応用できます.
他のライブラリからイージング関数を読み込む
一般にイージング関数の構造はライブラリによらず似たようなものになります.従って他のライブラリのイージング関数を上手くラップすることでSnap.svgから呼び出すことが可能になります.下の例ではjQuery Easing Plugin のコードに手を加え,Snap.svg用のプラグイン化したもの を使っています.
既存のイージング関数を利用する
既存のイージング関数を組み合わせることで,独自のイージング関数を定義することが出来ます.例えば,下のようにアニメーションの動きを逆方向とすることも可能です.
よく使うものは別途ライブラリ化しておいたほうが良いでしょう.
このようにSnap.svgが提供しているイージング関数はcssにおけるイージング関数とは互換性がありません.また,SMILのようなベジェ曲線による指定は出来ません.
アニメーション状況の取得
Element.animateメソッドが実行されると,引数に指定した内容でminaオブジェクトが生成されます.このminaオブジェクトはElement内部にアニメーション実行リスト†として格納されており,アニメーション終了・停止のタイミングでリストから除去されます.この現在実行中のアニメーション情報にアクセスするためのメソッドがElement.inAnimです.
Element.inAnim()
Element.inAnimメソッドは現在実行中のアニメーションの情報を返します.得られるデータは次のオブジェクトの配列です.(これは複数のアニメーションが同時に実行されうるためです.)
anim
Animationオブジェクト.アニメーション対象,duration値,easing,callback等のElement.animateメソッドの引数に相当する内容が格納されています.
curStatus
0〜1の値.0…アニメーション開始,1…アニメーション終了
status()
アニメーションステータス値の取得・設定関数
stop()
アニメーションを停止するメソッドです.
アニメーション実行中の判定
アニメーションが実行中かどうかはinAnimメソッドの実行結果で得られた配列のlengthを確認します.0以外だった場合,実行中のアニメーションが存在する事になります.
アニメーションの進捗状況を取得する
アニメーションの進み具合はElement.inAnimメソッドを実行した後,statusメソッドを実行します.
curStatusプロパティはinAnimメソッドを実行した時点での進捗を表します.
アニメーションの進捗状況を設定する
先ほどのstatusメソッドに0〜1の値を渡すことで,アニメーションの進捗状況を上書きすることが出来ます.
なお,アニメーション終了後にこのメソッドを実行しても何も起こりません.
アニメーションの個別停止
stopメソッドを実行することで,Element.stopメソッドと異なりアニメーション単体を停止することが出来ます.
Animationオブジェクトによるアニメーション
アニメーションを実行する場合,Element.animateメソッドにパラメータを個別に渡す方法の他,Animationオブジェクトを生成してから行うことも出来ます.Animationオブジェクトはアニメーションの設定を表します.
Snap.animation(attr, dur, easing, callback)
Snap.animationメソッドはAnimationオブジェクトを生成します.Animationオブジェクトの構造は次の通りです.
Animation.attr アニメーション対象の属性,及びアニメーション終了時の値
Animation.dur アニメーション継続時間
Animation.easing イージング関数
Animation.callback コールバック関数
Element.animate(animation)
Element.animateメソッドにAnimationオブジェクトを渡すことでアニメーションを実行することが出来ます.
Snap.svg 0.3.0では本メソッドにバグがあります.このページではパッチを充てています.コードを検索して次のように修正して下さい.
複数の要素にまたがるアニメーションを実行する
Element.animateメソッドは単一の要素に対するものでしたが,Set.animateメソッドを用いると複数の要素にまたがるアニメーションを実行出来ます.
複数の要素にまたがるアニメーションを実装する場合,素朴に考えると次のようになります.
もちろんこれで十分ですが,Set.animateメソッドで書き換えると次のようになります.
Set.animate(attr,dur,easing,callback/setting,...)
Set.animateメソッドは内部の要素に対してアニメーションを実行します.引数の内容はElement.animateメソッドと同じです.なお,callback関数は全てのアニメーションが完了した後に一度だけ呼び出されます.
なお,Set.animateメソッドに依るアニメーションは厳密には同期的ではありません.従ってシステムに負荷が掛かった場合等にごく稀にアニメーションがずれる可能性があります.この問題を回避するのであれば後述するSnap.animateメソッドを使いましょう.
※以前本メソッドを「同期的である」と解説していましたが,よくコードを読みなおしてみた処,そうとは限らないようです.
要素ごとにパラメータを変更する
要素ごとに異なるアニメーションパラメータを指定する事も可能です.attr,dur,easingを一つにしたものを可変数引数として渡します.
残念ながら現状要素ごとにアニメーションの開始時刻をずらす事は出来ないようです.
Snap.animateメソッドによる汎用アニメーション
先ほどまでは単一のElementオブジェクトないしはSetオブジェクトを基準にアニメーションを考えていました.Snap.animateメソッドを用いると,アニメーションの進捗を元に複数の要素を操作することが出来ます.アニメーションの同期が重要な場合,より高度な処理が必要な場合はこちらを使うほうが良いでしょう.
Snap.animate(from,to,setter,duration,easing,callback)
Snap.animateメソッドはアニメーションの開始値・終了値及びアニメーション期間,easing関数を元にsetter関数を呼び出します.
アニメーション制御オブジェクトの構造
Snap.animateメソッドは実行するとアニメーション制御オブジェクト(animオブジェクト)を生成します.このオブジェクトの構成は次の通りです.
anim.id
アニメーションID※内部値なので使う機会はないでしょう.
anim.duration()
duration値を取得・設定する関数です.
anim.easing()
イージング関数を取得・設定する関数です.
anim.speed()
アニメーションスピードを取得・設定する関数です。
anim.status()
アニメーションの進捗を取得・設定する関数です.
anim.stop()
アニメーションを終了します.
anim.pause()
アニメーションを一時停止します.
anim.resume()
アニメーションを再開します.
animオブジェクトの名称は便宜上のものです.Animationオブジェクトと役割を間違えないようにして下さい.
コールバック関数内部からアニメーションを停止する
anim.stopメソッドをアニメーションコールバック関数内で呼び出すと無限ループとなります. これは, anim.stopメソッドが自身のコールバック関数を呼び出すためです. 従って, コールバック関数内部から自分自身を停止する場合は, 何らかのフラグ値を定義しanim.stopメソッドの呼び出しを抑制する必要があります.
アニメーションの一時停止と再開
Snap.animateメソッドを使った場合,アニメーションを一時停止・再開することが出来ます.例を示します.
Snap.svg0.2.0ではresumeメソッドに問題があり,正しく動作しません.本ページではここ で公開されているパッチを適用しています.なおSnap.svg0.3.0で修正されました.
minaメソッドが定めるアニメーション
先ほどのSnap.animateメソッドでは時間経過を元にsetterメソッドに値が渡されました.この処理を行うためにSnap.animateメソッドでは内部でminaメソッドを呼んでいます.
mina(a,A,b,B,get,set,easing)
minaメソッドはアニメーション制御オブジェクトを生成します.メソッドを実行すると自動的にアニメーションキューにアニメーションが追加され,フレーム要求毎にget及びsetメソッドが呼び出されます.
a,Aにはb,Bから求まる値の範囲を指定します.b,Bにはアニメーション開始値とアニメーション終了値を指定します.getにはアニメーション進捗を算出するためのメソッドを指定します.通常は区間[b,B]の範囲の値を返す関数を設定します.setには区間[a,A]内の値が渡された際の処理を指定します.easingには区間[b,B]から[a,A]内の数値を算出するためのイージング関数を指定します.
minaメソッドの意味
Snap.animateメソッドをmina関数で書き換えると次のようになっています.内部でgetterとしてmina.timeメソッドが渡されています.
anim = Snap.animate(from, to, setter, duration, easing);
//↑は↓と同等(callbackを除く)
var now = mina.time();
anim = mina(from, to, now, now + duration, mina.time , setter, easing);
つまり,通常のアニメーションは時間という不可逆な基準を元に,(座標などの)相対的な値を求めてアニメーションを構成しています.minaオブジェクトを用いると,この「時間」とは別の基準(例えばスクロール量等)を基準に アニメーションを構成することができるのです.
下の例ではアニメーションの基準として別の要素の位置を使っています.
これはcssアニメーションやsmilアニメーションを起点にdomアニメーションを構成する(同期する)ことが出来るということでもあります.とは言え,本メソッドを使った場合,無用なメソッド呼び出しが頻発する結果になりかねません.イベント処理など,別のアプローチがないかよく考えてから利用しましょう.
mina.time()
mina.timeメソッドは現在時刻を取得します.Snap.animate等のメソッド内で利用されています.
minaメソッドの応用
minaメソッドはアニメーションのみならず,setTimeout関数やrequestAnimationFrame関数に置き換わる関数の自動実行機構として利用できます.getメソッドの値を固定することでアニメーションの実行を終了せずに継続させることが出来ます.この場合,アニメーションの終了にはanimオブジェクトのstopメソッドを利用します.
実際minaメソッドはこれらの関数を使って実装されています.
下記はこの動作をSnap.doメソッドとしてプラグイン化したものです.
minaメソッド利用時の注意点
Element.animate/Snap.animateメソッドによるアニメーションは指定した時間が経てば自動的に終了します.一方minaメソッドに依るアニメーションは先ほど示したように必ずしも終了するとは限りません.そのためアニメーションの必要がなくなったら,明示的にanim.stopメソッドを実行して animオブジェクトを開放して下さい.不用意にアニメーション対象を廃棄すると,無用なアニメーション処理が残ってしまいます.
プロパティ値の線形補間
Snap.svgが様々なプロパティ値をアニメーション化出来ているのは,元の値とアニメーション後の値との間を線形補間しているからです.この線形補間機能はドキュメントとしては公開されていませんが,次のようにスクリプトから呼び出すことが出来ます.
Element.equal(attrName, to)
Element.equalメソッドは現在のプロパティ値とtoで示したプロパティ値の間を線形補間するための基本情報を返します.attrNameには線形補間したいプロパティの名称を指定します.返されるオブジェクトの内容は次のとおりです.
from 現在のプロパティ(の行列表現)
to toで指定した値(の行列表現)
f (例えば計算結果の行列を)元の形式に変換する関数
この時,fromの中身とtoの中身は数値,もしくは同じサイズの数値配列 となっており,計算によってこの内容を線形補間することで,目的のプロパティに対する線形補間値が得られます.下の例では,プロパティ値の線形補間を容易にするElement.interpolateメソッドを追加しています.fillプロパティやpath文字列がequalメソッドによって配列に分解され,fメソッドにより元の形式に戻っていることがわかります.
Element.animateメソッドのカスタマイズ
Element.animateメソッドでアニメーション化出来ない,もしくは意図したアニメーションとならないものについてはこのElement.equalメソッドを拡張することで自由にアニメーションの内容を追加・変更することが出来ます.先ほど示した仕様に則り,from,to,fの3プロパティが定義されたオブジェクトを返す関数でElement.equalメソッドを上手く置き換えます.するとElement.animate側でそのメソッドを呼び出すため,アニメーションの内容が変化します.
次の例ではviewBox属性をアニメーション化可能としています.この場合viewBox属性の内容を配列化したものと,配列を文字列形式に変換する関数を返すようにします.
色アニメーションを変更する
Snap.svgでは色のアニメーションをRGB値の線形補間によって実現しています.この内容を色相値を用いたものに書き換えてみましょう.
アニメーションの実装サンプル
ここではSnap.svgならではのアニメーションサンプルについて示します.
pathの変形アニメーション
Snap.svgの高度なパス編集機構により,全く異なる形状間のモーフィング処理も数行で記述できます.
全く異なる形状間での変形はアニメーション内容を予測しにくいですが,pathのセグメント構成を一致させると動作をイメージしやすくなります.
なお,Snap.svgではElement.equalメソッドでも見たように座標の線形補間を前提としているため,それよりも高度な(例えば特定の頂点を円弧に沿わせるような)アニメーションを実現することは出来ません.
ドローツールを使ってアニメーション前後のパスデータを生成する場合は,頂点や制御点の数を変更せずに座標のみを変更するようにします.下の例ではアニメーションデータを外部のSVGファイルから取得しています.
この場合,グラフィックを固定部とアニメーション部とに分け,フレーム毎に必要となるアニメーションに関わる座標値の計算コストを下げるようにしましょう.
transform属性のアニメーション
transform属性のアニメーションも簡単に記述できます.
パスに沿ったアニメーション
SVGのAnimateMotion要素のようなパスに沿ったアニメーションはElement.getPointAtLengthメソッドとSnap.animateメソッドとを組み合わせて実現します.
イベント処理
SVGを構成する要素にイベントを登録すれば,よりインタラクティブなグラフィック表現が可能となります.
イベントに関わる関数
SVGはHTMLと同様にイベントによるスクリプトの実行をサポートしています.これにより何らかのユーザーの操作に対してSVGがグラフィカルに応答すると言った処理を実装できます.
以下にSnap.svgが提供しているイベントに関わるメソッドは次のとおりです.ほとんどがカーソル操作に関わるもので,いずれもSVGDOM標準のイベント処理をラップしています.
メソッド名 イベント内容 削除メソッド名 備考
Element.click クリック Element.unclick
Element.dblclick ダブルクリック Element.undblclick
Element.mouseover マウスオーバー Element.unmouseover
Element.mouseout マウスアウト Element.unmouseout
Element.mousedown マウスダウン Element.unmousedown
Element.mouseup マウスアップ Element.unmouseup
Element.mousemove マウスムーブ Element.unmousemove
Element.hover ホバー Element.unhover in/outの2関数を登録
Element.drag マウスドラッグ Element.undrag move/start/endの3関数を登録
Element.touchstart タッチスタート Element.untouchstart
Element.touchmove タッチムーブ Element.untouchmove
Element.touchend タッチエンド Element.untouchend
Element.touchcancel タッチキャンセル Element.untouchcancel
HTMLには他にもユーザーからの入力を前提としているイベント(keypress,keydown,keyup)があります.しかしSVGではその受け皿が存在しないため,仕様範囲外になっており(※ブラウザの種類によっては動作する事もあります),Snap.svgにおいても未サポートとなっています.
なお,これらのイベントはWindow,Document,HTMLInputElementオブジェクトで受け,その結果をSVGに適用するようにしましょう.
ハンドラ関数に渡される内容
上記関数の引数にはイベント発生時に実行したいハンドラ関数を指定します.その際に指定したハンドラ関数には,DOMでのイベントオブジェクトが渡されます.従ってDOMイベントに関わる一般的な知識が流用可能です.また,thisが指すオブジェクトはイベントを発生させたElementオブジェクトとなります.
イベント登録関数とイベント抹消関数
Snap.svgではイベントを登録する関数と抹消する関数がペアになっています.例えばElement.clickメソッドに対してはElement.unclickメソッドが対応します.
クリックに関わるイベント
ここではクリックに関わるメソッドについて示します.
Element.click(handler)
Element.clickメソッドはクリックイベントに関数を登録します.対象となる図形要素をクリックすると処理が呼び出されます.
Element.unclick(handler)
Element.unclickメソッドはクリックイベントから関数を抹消します.
Element.dblclick(handler)
Element.dblclickメソッドはダブルクリックイベントに関数を登録します.対象となる図形要素をダブルクリックすると処理が呼び出されます.
Element.undblclick(handler)
Element.unclickメソッドはダブルクリックイベントから関数を抹消します.
トグル機構を作る
オンオフ毎に処理を切り替えると言ったトグル機構を実装する場合は,Element.dataメソッドと組み合わせると便利です.下の例ではデータ「switch」に状態を記憶させています.
このような共通処理は予めapi化しておくと良いかも知れません.この例ではElement.toggleメソッドをライブラリに登録しています.
カーソル位置に関わるイベント
ここではカーソルの位置に関わるmousexxxメソッドについて説明します.
Element.mouseover(handler)
Element.mouseoverメソッドはマウスオーバーイベントに関数を登録します.対象となる図形要素の描画範囲にカーソルが入ると処理が呼び出されます.
Element.unmouseover(handler)
Element.unmouseoverメソッドはマウスオーバーイベントから関数を抹消します.
Element.mouseout(handler)
Element.mouseoutメソッドはマウスアウトイベントに関数を登録します.対象となる図形要素の描画範囲からカーソルが出ると処理が呼び出されます.
Element.unmouseout(handler)
Element.unmouseoutメソッドはマウスアウトイベントから関数を抹消します.
Element.mousedown(handler)
Element.mousedownメソッドはマウスダウンイベントに関数を登録します.対象となる図形要素の上でマウスボタンを押下すると処理が呼び出されます.
Element.unmousedown(handler)
Element.unmousedownメソッドはマウスダウンイベントから関数を抹消します.
Element.mouseup(handler)
Element.mouseupメソッドはマウスアップイベントに関数を登録します.対象となる図形要素の上でのマウスボタン押下が終了処理が呼び出されます.
Element.unmouseup(handler)
Element.unmouseupメソッドはマウスアップイベントから関数を抹消します.
Element.mousemove(handler)
Element.mousemoveメソッドはマウスムーブイベントに関数を登録します.対象となる図形要素の上でマウスカーソルを移動すると処理が呼び出されます.
Element.unmousemove(handler)
Element.unmousemoveメソッドはマウスムーブイベントから関数を抹消します.
複数のイベントを一括して扱うもの
ここでは複数のイベントをセットで扱うものについて示します.
Element.hover(handlerIn,handlerOut,contextIn,contextOut)
Element.hoverメソッドでは対象となる図形へのホバー処理を登録します.図形要素にカーソルが乗るとhandlerInが,カーソルが離れるとhandlerOutが呼び出されます.contextIn,contextOutにはhandlerIn,handlerOutへのthisオブジェクトを指定します.mouseover/mouseoutメソッドを一括して実行することに相当します.
Element.unhover(handlerIn,handlerOut)
Element.unhoverメソッドはhoverメソッドで登録した関数を登録リストから抹消します.
ドラッグ処理
SVGにおいてもドラッグ処理は有用です.Snap.svgではElement.dragメソッドを用いることで簡単にドラッグ処理を実現できます.
Snap.svgではドラッグ処理をHTML5でのdrag and drop apiではなく,古典的なmouseイベントによって実現しています.これはSVG1.1仕様にdrag and dropの規定が存在しないためです.なお,ブラウザによってはSVGにおいてもドラッグドロップapiが有効な場合があり,この場合はHTMLと同様の処理を実装することが出来ます.
Element.drag()
Element.dragメソッドは対象となる図形要素をドラッグ可能とします.
Element.drag(onmove,onstart,onend,ctxM,ctxS,ctxE)
Elementメソッドににハンドラ関数を登録しておくことでより高度なドラッグイベントを実現することが出来ます.それぞれドラッグ中,ドラッグ開始,ドラッグ終了時の処理を指定します.但しこの場合,デフォルトのドラッグ動作がキャンセルされてしまう 点に注意しましょう.
引数として渡した各ハンドラメソッドには異なるパラメータが渡されます.
onmoveに渡されるパラメータ
dx,dy ドラッグ開始時からのカーソルの移動量
x,y カーソルの現在位置†
e DOMのイベントオブジェクト
onstartに渡されるパラメータ
x,y カーソルの現在位置†
e DOMのイベントオブジェクト
onendに渡されるパラメータ
e DOMのイベントオブジェクト
†x,yはclientX,clientYに対応します.svg要素内部での座標ではありません.
デフォルトのドラッグ操作コードは次のようになっています.
なお,dragメソッドを複数回呼び出すケースは想定されていないようです.下のコードは一見正しそうですが,現状では上手く行きません.
Element.undrag(onmove,onstart,onend)
Element.undragメソッドはdragメソッドで登録した関数を登録リストから抹消します.
ドロップ操作を実現する
ドラッグ操作に対応するドロップ操作をサポートする機能は現状Snap.svgには存在しないようです.そのため,ドロップ操作はdragメソッドとhoverメソッドとを組み合わせて実現します.
ドラッグ中のオブジェクトを格納する変数を定義します.
ドラッグされる側ではドラッグ開始時に自身のpointerEventプロパティを"none"に設定します.これはドロップを検知する側でのhoverイベントを有効とするためです.
ドロップされる側ではhover-in時にドラッグ中のオブジェクトを記憶しておきます.この内容についてhover-out時にドラッグ処理が完了している場合に限り 処理を適用します.
動作の検証はドラッグ中のhover-in/out,未ドラッグ時のhover-in/outの4パターンで行なって下さい.下の例では,四角形をドラッグし円形にドロップすると,円の色に四角形が色付けされるという動作を記述しています.
タッチ操作に関わるメソッド
ここではタッチ操作に関わるメソッドについて示します.
Element.touchstart(handler)
Element.touchstartメソッドはタッチスタートイベントに処理を登録します.
Element.untouchstart(handler)
Element.untouchstartメソッドはタッチスタートイベントから処理を抹消します.
Element.touchmove(handler)
Element.touchmoveメソッドはタッチムーブイベントに処理を登録します.
Element.untouchmove(handler)
Element.untouchmoveメソッドはタッチムーブイベントから処理を抹消します.
Element.touchend(handler)
Element.touchendメソッドはタッチエンドイベントに処理を登録します.
Element.untouchend(handler)
Element.untouchendメソッドはタッチエンドイベントから処理を抹消します.
Element.touchcancel(handler)
Element.touchcancelメソッドはタッチキャンセルイベントに処理を登録します.
Element.untouchcancel(handler)
Element.untouchcancelメソッドはタッチキャンセルイベントから処理を抹消します.
動作の確認にはchromeのタッチスクリーンエミュレーション機能を利用しました.
Ajaxによる外部グラフィックデータの挿入
グラフィックデータは複数に分割して管理されることもあります.Snap.svgのAjax機能を使えば,非同期的に外部ファイルのグラフィックデータをwebページに取り入れることが可能になります.こうすることで,グラフィックとスクリプトコードが分離され,全体としての管理が容易になります.
Ajax機能によるSVGの読み込み
Snap.svgでは外部のグラフィックデータを読み込む手段としてSnap.loadメソッドとSnap.ajaxメソッドの二つが提供されています.いずれもテキストデータを読み込む点で同じですが,その役割が異なります.
Snap.load(url,callback,scope)
Snap.loadメソッドは(javascriptからアクセス可能な)任意のテキストファイル(SVGソースの一部分)を元にFragmentオブジェクトを生成します.urlには参照するファイルへのパスとファイル名を,callbackには読み込んだ後の処理を,scopeはcallbackに対するthisオブジェクトを指定します.
下の例では次のコードが記述されている「nodes.txt 」をSnap.loadメソッドを使って画面に表示しています.
]]>
本メソッドではプレーンテキストを扱いますから,テキストファイルの文字コードとしては原則utf-8を用いましょう.下記Snap.ajaxメソッドにおいても同様です.
Snap.loadメソッドがSVGファイルをロードできない
Snap.loadメソッドを用いてSVGファイルを読み込んだ際に上手く行かない場合があります.これは参照したSVGファイルにXML宣言/処理命令が含まれている 事に起因します.例を示します.
これらはグラフィック的には全く同じ内容ですが,後者はSnap.loadメソッドに失敗します.
内部的にはファイル内容を取得できているのですが,これをFragmentオブジェクトに変換する際に失敗しています .そのため後述するエラー処理の対象とならないどころか,エラーを発することもないため,原因の究明に手こずるおそれがあります.
そのためSVG文書全体を読み込みたい場合は,Snap.ajaxメソッドを用いたほうが良いでしょう.
Snap.ajax(url,[postData],callback,scope)
Snap.ajaxメソッドはhttpリクエストを発行し,得られたレスポンスを元にcallbackメソッドを呼び出すもので,主にXMLファイルの非同期読み込みに用います.urlにはリクエストの発行先,postDataにはhtmlリクエストに添付するポストデータ(object形式)を,callbackには読み込んだ後の処理を,scopeにはcallbackに対するthisオブジェクトを指定します.postDataは省略できます.callback関数の引数にはサーバー通信に際して生成されたXMLHttpRequestオブジェクトが渡されます.
プレーンテキストファイルの読み込み
一般的なテキストファイルを読み込んだ場合は,XMLHttpRequestオブジェクトのresponseTextプロパティにその内容が格納されます.
外部SVG画像の読み込み
SVGファイルをSnap.ajaxメソッドで読み込んだ場合は,XMLHttpRequestオブジェクトのresponseXMLプロパティにSVGDocumentオブジェクトが設定されます.従ってここからdocumentElementプロパティを参照することで,目的のグラフィックが定義されたsvg要素を取得します.例を示します.ここではSnap.ajaxメソッドで得られたSVGSVGEelemetオブジェクトをSnapメソッドを使ってPaperオブジェクトに変換しています.
この例ではSVG文書そのものをネストしたsvg要素として取り込んでいます.この場合,取り込んだ文書内にid属性やstyle要素が含まれていた場合に親文書の構造やスタイル設定を破壊します.従ってSVG画像の全体をそのまま取り込みたい場合はPaper.imageメソッドを使ったほうが無難です.例を示します.
XMLHttpRequestオブジェクトのresponseTextプロパティを元にuriデータスキーム形式のSVG画像データを生成し,Paper.imageメソッドを実行します.目的に応じて使い分けるようにしましょう.
上記は見た目上Paper.imageメソッドにSVG画像ファイルへのURIを直接指定した場合と同じ結果となります.しかし,生成したimage要素にSVG画像がテキストデータとして描きこまれていますから,生成したSVGグラフィックはそれ単体で動作可能となります.
何れにせよ,正しく外部リソースを読み込むためには,メソッドを呼び出している文書と指定したファイルのオリジンが同じである 必要があります.詳しくはXMLHTTPRequestオブジェクトの仕様を確認して下さい.
外部スタイルシートを参照していた場合
Snap.ajaxメソッドで読み込んだSVG文書内にxml-stylesheet処理命令や@import規則等による外部スタイルシートへの参照が含まれていた場合,これまでの例は正しく動作しません.
バイナリデータの取得
Snap.svgではバイナリデータの取得を想定していないようです.従って画像ファイルなどをバイナリ形式で扱いたい場合は独自のスクリプトを記述する必要があります.この場合Snap.svgのコードを直接編集してしまうのも一つの手です.
参考)Shadow DOMを利用したスタイルの分離
読み込んだSVG文書中にstyle要素が含まれていた場合, そのままPaperオブジェクトに挿入してしまうとメイン文書におけるスタイル設定が干渉し合い, 意図したグラフィックが得られない場合があります. このような場合はobject要素やiframe要素でグラフィックを描画すべきなのですが, 読み込んだグラフィックのスタイルを変更し難いといった欠点も持っています. この場合, Shadow DOM 機能を用いることで問題を解決できるかも知れません.
なお, Shadow DOMには様々な利用上の制限があり, 導入には細心の注意が必要です. 詳しくはこちら(Web Componentsの基本的な使い方・まとめ) を参照してください.
Ajaxとカスタムイベント
Snap.svgではAjax処理をカスタムイベント として実行しています.XHTMLHTTPRequestオブジェクトがHTTPステータスコードを返すと,それを元に「snap.ajax.[固有ID† ].[HTTPステータスコード]
」イベントを発生します.従ってこのイベントに独自の処理を登録しておくことで,より細かなAjax処理を記述できます.詳しくはカスタムイベントの項を参照して下さい.
†Snap.svgがイベント処理のために内部で発行しているIDで,それ以上の意味合いはありません.
エラー時の処理を登録する
Ajax機構による外部リソースの取得は常に成功するとは限らないため,エラー発生時の処理を定義しておきたい場合があります.Snap.svgでは専用のメソッドを定義していませんが,カスタムイベントを登録しておくことで対処することが可能です.例を示します.eve.onメソッドを用いてAjax機構に独自処理を挿入し,得られたHTTPステータスコードを元にエラー処理の実行有無を判定しています.
外部SVGリソース参照に応用する
SVGは様々な方法で外部のSVGファイルの内容を参照できるように設計されています.しかし,仕様解釈の違いからブラウザによって利用できる機能に違いがあります.この時,Snap.ajaxメソッドを用いるとこの互換性の問題を解決出来ます.
use要素による外部図形参照を実現する
use要素は同一ドキュメントのみならず,外部SVG文書に定義されている図形要素を参照できることになっています.しかし,この動作は現状では環境によっては上手く行きません.そこでこの動作をSnap.ajaxメソッドを使って再現することを考えます.
実現については様々な方法が考えられますが,ここでは単純に外部SVG要素の内容を直接参照元の文書に挿入することにしました.こうすることでuse要素は同一文書内の要素を参照することとなり,全てのブラウザでの動作が保証されます.なお,実際には文書内のID値が重複しないように考慮する必要があるでしょう.
SVGフォントのグリフ図形を利用する
SVGフォントはSVG文書の中でもフォントファイルとして機能するもので,主にfont要素,font-face要素,glyph要素から構成されています.このSVGフォントは通常ブラウザ互換性の点(firefox/ieで動作しない)から利用されることはまずありませんが,Snap.ajaxメソッドを使いグリフの形状を取り出して再利用出来ます.
下の例ではこのSVGフォントファイル から合字「ie」に対応するグリフ形状を抽出しています†.glyph要素をpath要素に変換する場合はまずd属性の内容をパス文字列として取得し,座標のとり方がy軸について反転していることから,Snap.path.mapメソッドを使って内容を鏡像変換します.この内容をpath要素に設定するのです.
†SVGフォントが複雑な構造をしているのであれば,font-face要素を参照することもあります.
]]>
img要素が参照するSVG画像の加工
通常スクリプトを使ってimg要素が参照しているSVG画像を編集することは出来ませんが,Snap.ajaxメソッドを用いるとこの中身を擬似的に操作可能です.以下に手順を示します.
img要素のsrc属性をSnap.ajaxメソッドに渡してPaperオブジェクトを生成します.
得られたPaperオブジェクトを使ってグラフィックを操作します.
PaperオブジェクトのtoStringメソッドを使ってSVGのソースコードを取得します.
encodeURIComponentメソッドを使い,SVGのソースコードをURIdataスキーム形式に変換し,元のimg要素に設定します.
カスタムイベントとカスタム属性
Snap.svgでは独自のイベント処理機構を内蔵しています.これを用いると柔軟なイベント駆動プログラミングが行えます.また,それに伴うカスタム属性の作り方について解説します.
eveオブジェクトによるカスタムイベント
eveオブジェクトはSnap.svg独自のイベント駆動プログラミングフレームワークです.予めハンドラ関数を登録しておくと,カスタムイベントを発行した際にバインドしておいた処理を呼び出すことができます.基本的な考え方はDOMが提供しているイベント機構と同等ですが,uiイベントを前提としていないため,より広範な範囲で利用できます.
eveはgithub 上でSnap.svgとは独立して公開されており,単独で利用することが出来ます.
簡単な例を示します.ここではeve.onメソッドで「mylogging」イベントに対する処理を定義しておき,eveメソッドで「mylogging」イベントを発行しています.
上記からイベント発行部とハンドラ呼び出しとがeveオブジェクトを介して緩やかに結合している事がわかります .こうすることで処理の呼び出し元の変更を伴わずに 複数のハンドラ関数の追加・削除や優先順位の指定が可能となるのです.
Snap.svgではこの機構をアニメーションやUIイベント処理などの様々な場面で利用しています.
このようにeveオブジェクトは有用ですが,無理に利用する必要はありません.ここでは後述するカスタム属性の定義を円滑に行うために説明しています.なお,実際にeve機構を使うのであれば事前にイベント名称や階層をきちんと設計しておきましょう.無闇に呼び出すとかえってコードの収集がつかなくなります.
eveオブジェクトが提供するメソッド
以下にeveオブジェクトが提供しているメソッドについて示します.
メソッド名 機能
eve.on() カスタムイベントにハンドラ関数を結びつけます.
eve() カスタムイベントを発行し,登録されているハンドラ関数を実行します.
eve.once() 一度だけ有効なカスタムイベントを定義します.
eve.off() カスタムイベントからハンドラ関数を除去します.
eve.unbind() eve.offメソッドのエイリアスです.
eve.stop() 以降のイベント発生をキャンセルします.
eve.nt() 現在実行中のイベント名を取得/確認します.
eve.nts() 現在実行中のイベント名を配列として取得します.
eve.f() カスタムイベントを発行する関数を生成します.
eve.on(name, handler)(zIndex)
eve.onメソッドはカスタムイベントを登録し,与えられたハンドラ関数とイベントとを結びつけます.
eve(name, scope, varargs)
eveメソッドはカスタムイベントを発行し,登録されているハンドラ関数を実行します.scopeにはハンドラ関数内部でthis変数が参照するオブジェクトを,varargsにはハンドラ関数へのパラメータを指定します.
eveメソッドはハンドラ関数による戻り値を配列として返します.ですが,イベント駆動型のプログラミングでは,呼び出し元はハンドラ関数によってどのような処理が為されたかを識る術はありません(戻り値が存在しない場合もあります).従ってこの戻り値に依存したプログラムが必要であれば,ハンドラ関数の指定に条件を設けるなどの設計を行うべきです.
イベント発行後の返り値が必要なケースとしては,エラーハンドリング処理等が考えられるでしょう.
Snap.svgではsnap.util.getattr
イベントにおいてハンドラ関数からの戻り値を取得しています.
イベント優先度の設定
eve.onメソッドはfunctionオブジェクトを返します.これは単一のカスタムイベントに複数のハンドラ関数が結び付けられていた場合にその優先順(zIndex値)を指定するもので,値の小さいものから順に実行されます.例を示します.通常は登録順に実行されるはずの処理の順を逆にしています.
なおこのイベントの発生順位は下で示すイベント階層を跨いで行われます.ハンドラ関数の処理順が重要な場合は全てにzIndex値を指定しましょう.
イベントの階層化
カスタムイベントを登録・呼び出す際,イベント名の区切り子として「.」もしくは「/」を指定するとイベントを階層化することができます.階層化されたイベントは左側(親)のイベントから順にマッチングされ,合致したものから順に実行されます.また,イベント登録時はワイルドカード「*」での指定が可能です.DOMイベントにおけるバブリング機構のような処理を行えます.
eve.once(name, handler)
eve.onceは一度だけ有効なイベントを登録します.eve.onで登録した場合と異なり,一度実行されると自動的にハンドラ関数の登録が破棄されます.アニメーションの実行処理などで利用されています.
eve.off(name, handler)
eve.offメソッドはonメソッドで登録したイベントを削除します.nameだけを指定すると,そのnameに登録されているものすべてが削除されます.
eve.off()を実行した場合,現在登録中の全てのイベントが削除されてしまいます .従ってSnap.svgの動作を破壊してしまいますから注意して下さい.
eve.stop()
eve.stopメソッドは現在実行中のイベントに対して現在実行中のハンドラ関数以降の処理をキャンセルします.
DOMイベントでのpreventDefaultメソッドに相当します.特定の条件下において規定とは異なる動作を取らせたい場合,先の優先度設定と組み合わせて利用します.
eve.nt()
eve.ntメソッドは現在の処理を実行するきっかけとなったイベントの名称を取得します.eve.ntsメソッドと異なり,単一の文字列として取得します.
eve.nt(name)
eve.ntメソッドに文字列を渡すと,イベント名称内に指定した文字列が含まれているかどうかを判定します.
eve.nts()
eve.ntsメソッドは現在の処理を実行するきっかけとなったイベントの名称を配列として取得します.
この仕組みを用いると,Element.attrメソッドで呼び出されたプロパティ名の一覧や,Snap.ajaxメソッドで返されたHTTPステータスコードを取得することが出来ます.
eve.f(name, varargs)
eve.fメソッドは指定したイベントを発生させる関数オブジェクトを生成します.この関数をElement.clickメソッド等に直接渡すことでDOMイベントとeveカスタムイベントとを直接バインドすることが出来ます.例を示します.ハンドラ関数には引数の末尾にDOMイベントを発生させたイベントオブジェクトが渡されます.
Snap.svgが利用しているカスタムイベント
Snap.svgが内部で利用しているカスタムイベントは,概ね名称が「snap
」で始まります.以下にその主なものを列挙します.
イベント名 処理・導入目的 備考
snap.util.getattr
属性値取得のためのイベント.
属性ごとの独自処理を行うためのサブイベント多数
snap.util.attr
属性値設定のためのイベント
属性ごとの独自処理を行うためのサブイベント多数
snap.ajax
外部リソース読み込みのためのイベント
snap.drag.start
図形のドラッグ操作のためのイベント
サブイベントとしてstart/move/endがある
snap.animcreated
Set.animateメソッドを実行するためのメソッド
mina.finish
アニメーション完了時のイベント
mina.stop
アニメーション中断時のイベント
従ってこれらのイベントを上書き,ないしは削除してしまうとSnap.svgの動作を破壊してしまいます .逆に上手くイベント処理を追加することでSnap.svgをより柔軟にカスタマイズすることが可能となります.その際たるものが次に紹介するカスタム属性 のテクニックです.
カスタム属性の定義
Element.attrメソッドはこのeveオブジェクトを用いて実装されています.従って,この仕組みを利用することでカスタム属性 を定義することが出来ます.
カスタム属性とは
SVGでは要素ごとに位置や塗り潰し等の様々な属性が定義されており,Snap.svgではこの属性値を書き換えることでグラフィックを描画しています.カスタム属性とはSVGには定義されていない独自の属性に対して,新たにSnap.svgでの振る舞いを設定したもの です.
カスタム属性は実装が比較的簡単ながら,Element.attrメソッドから既存のSVG属性と同様に扱える等扱いやすく,再利用性も高いといった特徴をもっており,Snap.svgを使いこなすのであれば是非マスターしておきたいテクニックです.
この「カスタム属性」のテクニックはSnap.svgのドキュメントにおいては明言されていないものの,実際のコード内部では多量に使われています.従ってカスタム属性を自作する場合はSnap.svgのコードを参考とすると良いでしょう.
カスタム属性の導入場面
カスタム属性が有効なケースとしては次のような場合が挙げられます.
path要素に意味付けを行い,新しい図形オブジェクトを定義する場合
よく知られた図形であれば,大抵は数個のパラメータを与えることで図形の形状が確定します.従って,このパラメータをカスタム属性として定義し,その内容からパス図形を描くようにすると図形の再利用が可能になります .
Snap.pluginメソッドの項で扇型オブジェクトを定義する例を示します.
g要素に意味付けを行い,複数のオブジェクトをまとめた新しい図形オブジェクトを定義する場合
上の例と同様ですが,カスタム属性を定義する対象をg要素とし,カスタム属性設定時に中身を書き換えるようにします.こうすることで複数の図形からなるグループをあたかも単一のオブジェクトとして扱えます.
複雑なオブジェクト生成を要する場合
図形要素のみならず,グラデーション等の複雑な要素生成が必要となるケースにおいては,カスタム属性を使ってその処理をひとまとめにしておくと後々のコードの再利用が楽になります.
アニメーションの制御を行う
複数の属性にまたがるアニメーションを作る場合,アニメーションの進捗値に対応するカスタム属性を定義し,その内容に沿って図形のプロパティを書き換えるようにします.こうすることで単一のカスタム属性をアニメーション化するだけで高度な動きを実現することが可能となります.
このようにSVG部品を定義してライブラリ化する際に非常に有効です.
カスタム属性の定義
カスタム属性を定義するには次の手順を踏みます.
eve.oneメソッドを使い,「snap.util.attr.[カスタム属性名]
」イベントに対する処理を登録する.
必要に応じeve.onメソッドを使い,「snap.util.getattr.[カスタム属性名]
」イベントに対する処理を登録する.
カスタム属性の動作原理
Element.attrメソッドはSVGの要素に対して属性値を書き込んでいますが,その仕組みは次のとおりです.
事前にeve.onメソッドを使って共通イベント「snap.util.attr/getattr
」が定義されています.
このイベントには属性名と値からsetAttribute/getAttributeメソッドを実行する処理が登録されています.
Element.attrメソッドが呼び出されると,引数の内容から「snap.util.attr/getattr.[属性名]
」イベントを発行します.
すると先ほど登録しておいた一階層上のイベント「snap.util.attr
」が発生し,登録しておいたメソッドが実行されます.
その結果,domが書き換えられるのです.
このことから,「snap.util.attr.[カスタム属性名]
」イベントに対する処理を登録することで,Element.attrメソッドから独自の処理を呼び出せるようになります.この時eve.onメソッドに渡す関数には,Element.attrメソッドによって,this変数としてElementオブジェクトが,引数としてattrメソッドで渡したパラメータが設定されます.例を示します.ここではmyAttribute
属性を定義しています.
このようにdomへの書き込みを行いつつ,独自の処理を実行することが出来ました.
なお,eve.onメソッドの呼び出しは一度で十分なため,よく使うカスタム属性についてはプラグインモジュールとしてまとめておくとよいでしょう.
イベント処理の優先順位を意識する
カスタム属性の定義はこれだけでも十分ですが,より高度な処理を行いたい場合はその優先順について意識する必要があります.Element.attrメソッドによって実行される処理の順番は次のとおりです.
優先度:-1 「snap.util.attr/getattr.[属性名]
」イベント
特殊なパラメータ設定が必要な場合に対する処理.mask,transform等
優先度: 0 「snap.util.attr/getattr
」イベント
共通の属性値設定/取得処理.domの内容を設定/取得します.
domへの属性値設定・取得を抑制する
通常Element.attrメソッドはdomへの値の書き込みを行います.が,カスタム属性に対する処理の優先度を上げ,以降のイベント発生をキャンセルさせることでdom操作をスキップすることが出来ます.この場合,値の設定/取得の双方のイベントに処理を登録します.
既存の属性値設定処理をオーバーライドする
特定の属性には専用のイベント処理が優先度-1で定義されています.従ってこの内容を上書き(オーバーライド)するのであれば,優先度を−2としたイベント処理を登録し,以降のイベント発生をキャンセルするようにします.
例を示します.ここではclip属性に対して独自処理を追加しています.特定の内容が渡された時に限り独自処理を実行し,通常のデータ(Element)が渡された場合はそのままの処理としています.
domへの値設定処理後に処理を実行する
逆にdomへの値が描きこまれたあとで処理を行うのであれば,登録するイベント処理を優先度1に設定します.複数の属性値が絡み合って一つの処理を行う場合はこの方法が便利です.
カスタム属性とアニメーション
独自に定義した属性についてもElement.animateメソッドを使ってアニメーション化することが出来ます.この特徴を応用するとアニメーションの表現力が格段に向上します.
リストデータを採るカスタム属性をアニメーション化する
カスタム属性は通常何もせずともアニメーション化可能ですが,これは属性の値が数値である場合に限ります.従って数値リスト等の文字列データをアニメーション化したい場合は,上記の他にElement.equalメソッドを拡張する必要があります.
下の例では値として数値のリストを受けるmyAttrカスタム属性をアニメーション化しています.myAttr属性の値についてElement.equalメソッドを拡張し,文字列と配列との相互変換が行われるようにしています.
カスタム属性を元に他の属性をアニメーションさせる.
Element.animateメソッドは通常単一の属性のみをアニメーション化しますが,カスタム属性への値の変更を起点に他の属性を書き換えるようにすると,より複雑なアニメーションを実現可能です.
下ではこの基本動作をプラグイン化したものです.Element.setCustomAnimateメソッドにアニメーション関数を登録すると,Element.customAnimateメソッドはその内容を元にアニメーションを実行します.この動作を実現するためにカスタム属性animationProgressを定義しています.
プラグインによる機能の拡張
Snap.svgも他のjavascriptライブラリと同様に拡張性をもった構造をしており,独自機能をSnap.svgに登録することが出来ます.各自の環境に合わせて使いやすいプラグインライブラリを作成しましょう.
Snap.pluginメソッドで機能を拡張する
Snap.pluginメソッドを使うと既存のライブラリに自作の関数等を追加することが出来ます.
Snap.plugin(register)
Snap.pluginメソッドはSnap.svgに独自の関数を追加します.Snap.svgのライブラリを拡張するように,拡張処理登録用のファンクションを引数に渡します.するとSnap.pluginメソッドにより関数が自動実行されます.
登録用関数の内部ではそれぞれのprototypeオブジェクトに共通関数を設定していきます.その際,既存のメソッドやオブジェクトを上書きしてしまわないように注意して下さい.
例を示します.ここではSnap.pluginメソッドを使って新たにElement.linkingメソッドを作っています.
実はSnap.svg自体がこのプラグイン機能を使って自分自身を拡張していますから,使い方が判らない場合はSnap.svgのコードそのものを参考としても良いでしょう.
プラグインを大量に導入する場合,名前の衝突のおそれがあります.そのためSnap.svgが普及するにつれて,プラグイン作成について何らかのルール作りが必要となるでしょう.
Snap,Paper,Element以外のオブジェクトを拡張する
先ほどの例から判るようにSnap.pluginメソッドが主眼においているのはSnap,Paper,Elementオブジェクトです.従ってSet/Fragment/Animationオブジェクトを拡張するには少し細工をする必要があります.例えばSetオブジェクトを拡張する場合,一旦Setオブジェクトのインスタンスを取得し,そのコンストラクタ関数のprototypeオブジェクトを見つけます.以降はElementオブジェクト等と同じようにprototypeオブジェクトに機能を追加していくだけです.例を示します.
この通り,実はSnap.svgを拡張するのにSnap.pluginメソッドを使う必要は無いのですが,無闇に拡張を繰り返すとコードの管理が面倒となるため気をつけましょう.
prototypeオブジェクトの取得についてはSnap.set().__proto__
でも同じ結果になります.が,互換性に難があります.
既存メソッド拡張の一般形
既存のメソッドを拡張をする場合,よく用いられる方法として元々のファンクションをラップして置き換えるものがあります.
var originalFunc = Element.prototype.someFunc;
Element.prototype.someFunc = function(){
//your customized codes
originalFunc.apply(this, arguments);
return this;
}
この場合はSnap.svgのコードをよく読んで引数の内容と返り値の形式を一致させるようにします.この方法は単純であるものの,Element.attrメソッド等,はっきりとした引数を取らない関数を拡張する際にコードが煩雑化します.javascriptに慣れない内は手を出さないほうが無難です.
Element.attrメソッドの拡張
Element.attrメソッドはカスタム属性の追加により機能を拡張します.詳しくはeveの項を参照して下さい.
独自の図形オブジェクトを定義する
最後にここまで紹介したapiを駆使し,アニメーション可能な独自図形を作ってみることにします.
角度指定可能な扇型オブジェクトを作る
Snap.svgでは扇型を描画するためのパス文字列の拡張が為されていますが,これをアニメーション化するとなると思い通りに動かせないと言った難点があります.そこでパラメータとして角度を指定可能な扇型オブジェクトを新たに定義することにしました.
ポイントとしては次の点が挙げられます.
Paperオブジェクトにpieメソッドを追加する.
基礎となる図形要素としてはpath要素を用いる.
カスタム属性としてx,y,r,start,endの5つを用いる.
これらはattrメソッドからアクセス可能とする.
他の図形オブジェクトを作成する場合もほぼ同様です.
以下に実際のコードを示します.いずれも難解な部分は無く,すっきりと記述できていることがわかります.