Snap.svgの使い方 まとめ api
[重要]お知らせ・2016/10/27
auonenetホームページ公開代理サービス終了(2017/10/31)に伴い, 本ページはこちら(http://defghi1977.html.xdomain.jp/tech/snapsvg/snapsvg.xhtml)に移動しました.
誠にお手数ですが, ブックマーク・リンクなどにおける参照URLの更新をお願いいたします.
※なお, こちらのページはサービス終了まで残しておきますが, 以降のメンテナンスの対象外(忘れるかも…)となります.
This page was moved to here. Please update URL settings of bookmarks, links or more. Thank you.

Snap.svgの使い方 まとめ How to useすなっぷ.svg タイトル svg要素の基本的な使い方まとめ・別館

written by DEFGHI1977@xboxlive

本記事ではSVG世代のjavascriptライブラリとして有望なSnap.svgの使い方について実際に動作しているサンプルを交えて解説しています(これは動作サンプルであると同時にテストコードと言えるものですね).なお間違いがあったり,バージョンアップに伴う不整合が発生するかも知れませんので,その点ご容赦下さいね.やっぱり手でゴリゴリ書いてます.やっぱりSVGは手書きしてなんぼ!

なお,Snap.svgを用いたSVG(もしくはjavascriptの)入門記事ではありません.Snap.svgを上手く活用したいのであれば,背後で動いているsvgについてよく知っている必要があります.そのため,ある程度SVGやプログラミングの知識があることを前提に話を進めていきます

記事の作成にあたってはSnap.svg version 0.3.0を用いました.動作の検証はfirefox31・chrome33/xubuntu14.4で行っています.ie11でも概ね良好なようです.

※ドキュメント的にかなりのボリュームがあり,動作サンプルも多数収録していることもあり,計らずも非常に重いページになってしまいました.出来ることならchrome等の軽いブラウザで見て頂けるとストレスが少ないかと思います.

※本ページのスクリプトには一部エラーを発生する部分があります.これはスクリプトが正しく動作しないケースを提示しているためであり,予めご了承ください.

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ですが,公式のドキュメントがあまり役に立ちません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が提供している機能です.

つまり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は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).安定版には存在しない,様々な機能強化がなされていますが,安定には程遠いため参考程度に留めましょう.

動作環境

このようにSnap.svgは内部でSVGを用いていますから,少なくともSVGを解釈可能な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を使うと言ったように使い分けると良いでしょう.

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);
  }

]]>

動作原理は次のとおりです.

動作の確認

Snap.svgの組み込みが完了したら,動作の確認をします.先程の文書において,ライブラリの参照を指定したscript要素よりも後に下記のコードを記述し,webブラウザで開いてみましょう.


]]>

このようにグラフィックが描かれたらSnap.svgが正しく動作しています.上手く行かない場合は,デバッグツールなどを用いて正しくモジュールが読み込めているか,コードの内容に不備がないか確認して下さい.

Snap.svgを活用するコツ

このままSnap.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,eve,minaの3つです.他のjavascriptライブラリを利用する場合やグローバル変数を定義する際は,名称が競合しないようにして下さい.

以下これらの動作について説明していきますが,必ずしもオブジェクトごとに説明していくわけではありませんのでご注意下さい.

グラフィックを描画する流れ

Snap.svgを使ったグラフィック描画処理は概ね次の流れに沿って行います.

  1. スクリプトの実行トリガーを決めます.
    何らかのイベントに対してグラフィック描画処理を登録します.例えば次のイベントが候補に上がるでしょう.
    • windowのloadイベント

      最も一般的なタイミングです.文書の全ての描画が完了した上でスクリプトが実行されるため,安定した結果が得られます.その代わり,余計な再描画処理が発生するため動作パフォーマンスに劣ります.出来ることなら下のDOMContentLoadedイベントを利用しましょう.
    • documentのDOMContentLoadedイベント

      DOM解析が完了したタイミングで実行されるため,余計な再描画処理が発生しにくく動作が軽快になります.特に問題が無い限りはこの方法が良いでしょう.
    • clickイベント等

      任意のタイミングで処理を実行します.全ての処理をこの中に記述してもよいですが,イベントが発生する頻度を鑑みて,予め事前処理を行っておく等の工夫をしましょう.
    • script要素のロードタイミング
      この場合は,必ずSnap.svgを読み込んだ後で自作のスクリプトが実行されるようにして下さい.なお,DOM解析が完了していないタイミングでスクリプトを実行していますから,svg要素の取得や作成したsvg画像の挿入先等に制約が発生することになります.
      またscript要素のdefer属性によってライブラリ読み込みの高速化を図っていた場合,上手く動かないケースが発生します.
  2. Paperオブジェクトを取得します.
    上記で登録した関数内部でPaperオブジェクトを取得します.以降このPaperオブジェクトに対してグラフィックの追加を行っていきます.
  3. グラフィックの描画/外部グラフィックの読み込み
    Span.svgのもつapiを元にグラフィックを定義・描画していきます.

Paperオブジェクトの取得

Paperオブジェクトを取得するには次の何れかを実行します.

Snap(width, height)

新たにSVGグラフィックを生成する場合に利用します.この例は先ほど示しました.svg要素を新たに生成し,body要素に挿入します.引数に与えたwidth,heightはsvg要素のwidth,height属性に割り当てられます.

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要素にアクセス可能です.

Paperオブジェクトの特徴

Paperオブジェクトはsvg要素に対応するもので,後述するElementオブジェクトでもあります.従って他のElementオブジェクトにはない特色を持っています.

  • グラフィックを描画するためのメソッドを提供します.
    これについては次のセクションで解説します.
  • Paperオブジェクトをネストすることが出来ます.
  • viewBoxプロパティによりグラフィックの描画単位を定義します.
  • Elementオブジェクトのメソッドの一部に無効となるものがあります.
  • 最も外側のPaperオブジェクトではHTMLのスタイルが有効となります.

ネストした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を自由に呼び出すことが出来ます.が,要素ごとに有効となる属性等が異なるため,細かな部分での使い勝手が変化する点に注意しましょう.

前置きはここまでとして,以下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メソッドは任意のパス図形を描画します.

引数としてはパス文字列の他,Snap.parsePathStringメソッドから得られる配列データを渡すことが出来ます.

この配列データはpath文字列にSnap.parsePathStringメソッドを施すことで得られます.セグメントごとにパスを操作する場合に非常に重宝します.

json形式での描画

引数にjson形式のデータを渡すことも出来ます.スタイル情報を一括指定することが可能です.パス文字列はpathプロパティに割り当てます.

パス文字列で使えるコマンド群

以下にSnap.svgで扱えるパスコマンドの一覧を示します.SVGでのパスコマンドに加え,Snap.svg専用のパスコマンドが使えます.

パスコマンドの一覧
コマンド 意味
絶対 相対
Mm位置のスキップ(x y)
Zz図形を閉じる
Ll線分を引く(x y)
Hh水平線分を引く(x)
Vv垂直線文を引く(y)
Cc3次ベジェ曲線を引く(x1 y1 x2 y2 x y)
Ss連続した3次ベジェ曲線を引く(x2 y2 x y)
Qq2次ベジェ曲線を引く(x1 y1 x y)
Tt連続した2次ベジェ曲線を引く(x y)
Aa円弧を引く(rx ry rotation large-arc-flag sweep-frag x y)
RrCatmull-Rom曲線(x y)†
Oo楕円(rx,ry)†
Uu円弧/扇型(r,start,end)†
†はSnap.svgの拡張コマンド

Snap.svg拡張パスコマンド

Snap.svgでは下記に示す「R」「O」「U」コマンドを利用可能です.何れもSVG標準のコマンドでは描きにくい図形を簡単に定義できます.

Rコマンド:Catmull-Rom曲線

Snap.svgではパス文字列の拡張としてコマンド「R」が追加されています.Mコマンドで起点を指定しRコマンドでなめらかに繋げたい座標を列挙します.

Oコマンド:楕円

Snap.svgではパス文字列の拡張としてコマンド「O」が追加されています.コマンド実行時の終点を中心とし,楕円を描きます.12時の方向から時計回りに一周します.

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/hangingmiddle/hangingend/hanging
start/middlemiddle/middleend/middle
start/ideographicmiddle/ideographicend/ideographic

パスに沿ったテキストの配置

Element.attr({textpath: pathStr})

Paper.textメソッドで得られたtextオブジェクトにtextpathプロパティを指定するとパスに沿ったテキスト配置が行えます.

textpathプロパティにはパス文字列の他にSnap.pathメソッドで得られたpathオブジェクトを渡すことも出来ます.

画像の読み込み

Paper.imageメソッドを使うと,外部画像を読み込む事が出来ます.

Paper.image(src,x,y,width,height)

Paper.imageメソッドはsrcに指定した画像を描画します.DOMでは(SVGの)image要素(SVGImageElement)を生成します.

画像のサイズを省略した場合,画像のサイズで描画されます.

本メソッドは画像を静的なものとして表示します.外部SVG画像を読み込んだ後,その内容を書き換えたい場合はSnap.load/Snap.ajaxメソッドを用いてSVGのDOM構造をインポートするようにします.

json形式での描画

引数にjson形式のデータを渡すことも出来るはずなのですが,現在のバージョンでは正しく動作しません.

この問題は一旦空のimageオブジェクトを生成してからattrメソッドを実行することで回避できます.

より詳細な画像読み込み処理

Paper.imageメソッドが提供している機能はあくまで簡易的なものです.外部画像読み込みの際,読み込み完了・失敗の判定が必要な場合はSVGDOMのload/errorイベントを利用します.

画像ロード完了時に処理を実行する例

画像ロード失敗時に処理を実行する例

描画オブジェクトのグルーピング

これまで生成してきた描画オブジェクトをグループ化すると,複数のオブジェクトを一括して管理することが可能になります.

Paper.g(elem1, elem2, ...)

Paper.gメソッドは描画オブジェクトをグループ化するコンテナオブジェクトを生成します.

gオブジェクトの中身は後から追加することも出来ます.

Paper.group(elem1, elem2, ...)

Paper.groupメソッドはPaper.gメソッドのエイリアスです.

gオブジェクトの振る舞い

gメソッドで生成したElementオブジェクトはPaperオブジェクトのように振る舞います.従って次のようにPaperオブジェクトのもつapiを呼び出すことが可能です.

図形のスタック

上記に示したメソッドを実行していくとPaperオブジェクトに対応するsvg要素に図形要素が積み重なっていきます.SVGでは要素が現れた順に図形を描画していくので,この順番を変更すると図形の重なり合いを変更できます.例えば下の例では先に生成したcircle要素の前にrect要素を挿入し,グラフィックの上下関係を変更しています.

グラフィックのクリア

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")で囲むようにします.

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…グラフィックの不透明度に関わる属性です.

特殊な属性

以下は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におけるスタイルとはグラフィックの見た目,つまり色やパターンを指します(それ以外のグラフィックの配置や形状に関わるものを含みません).

例を示します.予め文書内に次のようなstyle要素を記述しておきます(link要素を使って外部CSSファイルを読み込んでも構いません).ここではクラスclassAclassBに対するスタイルを定義しています.なおfill,stroke,stroke-widthはSVGの要素に対してのみ有効なプロパティになります.


	.classA{fill:red; stroke:orange; stroke-width:15px;}
	.classB{stroke-dasharray:30;}
]]>

その上でこのクラス名を図形要素に指定すると,指定したスタイル(グラフィック)の内容で図形が描画されます.

こうすることでSVGの出力処理をグラフィックの構造を定義する部分(スクリプト)とグラフィックの見た目を制御する部分(スタイルシート)とに分離できます.するとコードが分割されたことによりそれぞれの内容が簡潔となり,後々のコードの再利用やメンテナンスがやりやすくなります.

クラス値の制御を行う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メソッドは指定したクラスが現在この要素に適用されているかどうかを判定します.

スタイルの優先順位とElement.attrメソッド

SVGでは色などのスタイル情報を複数箇所で指定することが出来ますが,その優先順は次のとおりです.

  1. style属性
    SVG要素のstyle属性として直接列挙されるもの.最も優先順位が高い. get/setAttribute("style",...)メソッドの他,SVGElement.style.xxxからアクセスすることができます.
  2. style要素/link要素によるcssファイルの外部参照
    style要素内で定義したものです.
  3. プレゼンテーション属性
    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プロパティを直接操作します.

グラフィック定義情報とuse要素

SVGでは各種定義情報を格納するdefs要素や図形の複製を行うuse要素があります.Snap.svgではこれらの要素にアクセスするために専用のメソッドが提供されています.

Paperオブジェクトの内部構造

Paperオブジェクトが作られると,対応するsvg要素に自動的にdesc要素とdefs要素が作られます.


   Created with Snap
   
       
   
   
]]>

この時,defs要素内には様々な定義情報(グラデーションやテンプレート等)が格納されていきます.下記に示すメソッドはこのdefs要素へのアクセスを提供します.

Element.toDefs()

Element.toDefsメソッドは現在のオブジェクトをテンプレートとするためにdefs要素に移動します.下にイメージを示します.専ら下のuse要素と組み合わせて利用することになるでしょう.


   Created with Snap
   
   
   
   Created with Snap
   
       
   
]]>

Element.use()

Element.useメソッドは現在のオブジェクトへの参照をもつuse要素をsvg要素に追加します.

下の例ではテンプレートとなる図形要素を定義し,それをuse要素でリンクしています.その後でテンプレートとなった図形オブジェクトをdefs要素に移動しています.なお,use要素はx属性,y属性によりグラフィックの描画位置をずらすことが出来ます.

svg要素間のノードの移動について

gradient機能やpattern機能によって(暗黙的に)生成されたグラフィック構成要素は原則その要素を生成したPaperオブジェクト内で管理されます.従って,単一の(HTML)文書内に複数のsvg要素が存在する場合,これらの要素間で図形要素の移動を行うとdefs要素との相関が壊れてしまいます.この問題はPaper.toStringメソッドなどを用いてSVGソースコードを抽出する際に問題となるでしょう.

use要素の直接生成

Paper.useメソッドを使うと直接use要素を生成することが出来ます.

Paper.use(element/id)

Paper.useメソッドは引数に与えた要素を参照するuse要素を生成します.

defs要素配下への暗黙的な移動

Snap.svgが提供するメソッドの中には暗黙的にElementの位置が移動するものがあります.下記のメソッドは何れも実行した際にElementオブジェクトがdefs要素配下に配置されます.

  • Element.toDefs
    Elementオブジェクトそのものがdefs要素配下に移動します.
  • Element.attr({clipPath/mask:element})
    引数として与えたElementオブジェクトがdefs要素配下に移動します.
  • Paper.gradient
    生成したgradientオブジェクトがdefs要素配下に配置されます.
  • Element.pattern
    生成したpatternオブジェクトがdefs要素配下に配置されます.また,Elementオブジェクトはpattern要素配下に移動します.
  • Element.marker
    生成したmarkerオブジェクトがdefs要素配下に配置されます.また,Elementオブジェクトはmarker要素配下に移動します.
  • Paper.filter
    生成したfilterオブジェクトがdefs要素配下に配置されます.

要素の配置位置はセレクタによる検索処理に影響しますから,defs要素配下への移動が発生するものについては注意深く扱いましょう.

ペイントサーバーの定義

ペイントサーバーは塗り潰しや線の中身を指します.通常は単色ですが,以下に示す機能を使えばより効果的なグラフィックとなります.

グラデーション

SVG標準のグラデーション定義は御世辞にも単純とは言えません.Snap.svgでは簡単なグラデーション記述を行うことで簡潔にグラデーションを定義できます.

Paper.gradient(gradientStr)

Paper.gradientメソッドはグラデーションによるペイントサーバーを生成します.本メソッドで生成したオブジェクトはfillプロパティやstrokeプロパティ等に直接指定できます.本メソッドで生成したguradientオブジェクトはPaperオブジェクトに対応するsvg要素に格納されます.

グラデーションオブジェクトの再取得

グラデーションを施したfill/strokeプロパティの内容をattrメソッドで取得するとurl(#[id])形式の文字列が返されます.そのため,グラデーションの色を変えたい場合などで再度gradientオブジェクトが必要な場合は次のようにします.

グラデーション記述の書式

グラデーションは次のように指定します.

  • 線形グラデーション(objectBoundingBox)
    1. 接頭辞を「l」とします.
    2. グラデーション対象の図形のbounding boxを[0,0]×[1,1]の矩形範囲に見立ててグラデーション方向を指定します.
    3. 色の間には-を挿入します.
    4. ストップオフセット値は色の後に:[offset値]と指定します.
    例)l(0,0,1,0)black-white:50-black
  • 線形グラデーション(userSpaceOnUse)
    1. 接頭辞を「L」とします.
    2. グラデーション対象の図形が描かれている座標系を基準にグラデーション方向を指定します.
    3. 色の設定は上に同じです.
    例)L(100,0,200,0)red-blue:25-red:75-blue
  • 円形グラデーション(objectBoundingBox)
    1. 接頭辞を「r」とします.
    2. グラデーション対象の図形のbounding boxを[0,0]×[1,1]の矩形範囲に見立ててグラデーションの中心と半径を指定します.
    3. 色の設定は上に同じです.
    例)r(0.25,0.5,0.5)green-yellow
  • 円形グラデーション(userSpaceOnUse)
    1. 接頭辞を「R」とします.
    2. グラデーション対象の図形が描かれている座標系を基準にグラデーションの中心と半径を指定します.
    3. 色の設定は上に同じです.
    例)R(200,300,200)white-yellow:50-red:51-orange

グラデーション内容へのアクセス

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要素におけるビューボックスに対応します.タイル図形はこのビューボックスの描画範囲から生成されます.

マーカーによる線の装飾

マーカーは線の方向やパス上の位置を指し示す役割を持っています.適切に利用することでより判りやすいグラフィックとなるでしょう.

Snap.svgとマーカー

Snap.svgではElement.markerメソッドがマーカー定義機能を担っています.

Element.marker(x,y,width,height,refX,refY)

Element.markerメソッドは既存の図形をマーカーとして利用可能とします.x,y,width,heightから定まる矩形がマーカーに対するviewBoxに割り当てられます.省略すると自動的に図形のbounding boxが指定されたものとして扱われます.refX, refYはカーソル画像を描画する基準の位置を指定します.

生成したmarkerオブジェクトは図形オブジェクトのmarker-start/marker-mid/marker-end属性に指定します.例を示します.

確かにマーカー図形が描画されましたが,このままでは図形が大きすぎるなど見た目がよくありません.

マーカーの制御

このように既存の指定だけでは余り使い勝手がよくありません.そこで,より目的に叶ったマーカーとするためにmarkerオブジェクトに次の属性を追加します.

markerWidth,markerHeight
マーカーの大きさを指定します.
markerUnits
マーカーの大きさ(markerWidth,markerHeight)をどこに揃えるかを指定します.
userSpaceOnUse
現在の座標スケールに合わせます.
strokeWidth
stroke-width値倍に合わせます.
orient
マーカーの方向を指定します.
overflow
viewBox範囲外を描画するかどうかを指定します.
visible
描画します
hidden
描画しません.

例えばマーカーの大きさを15px平方に固定する場合は,次のようにします.

画像の合成

複数の画像を合成する場合,Snap.svgでは専用のメソッドが提供されており,記述が非常に少なくて済みます.

クリップパスによる画像クリップ

Element.attr({clipPath: shape})

Snap.svgでのクリップパスの定義は簡単です.予め図形要素を生成しておき,それをclip-path属性に渡すだけです.

マスクによる画像の合成

Element.attr({mask: element})

Snap.svgでのマスク操作もクリップパスの設定と同様に簡単です.予め図形要素を生成しておき,それをmask属性に渡すだけです.

なお,複数の図形要素から構成されるマスクを定義する場合は次のPaper.maskメソッドを利用します.

Paper.mask(attr)

Paper.maskメソッドはmask要素に対応するElementオブジェクト(maskオブジェクト)を生成します.必要があれば引数にmask要素に対する属性値を指定できます.なおmaskオブジェクトはPaperオブジェクトと同じように振る舞うため,次のように図形要素を直接挿入できます.

Paper.mask(element...)

Paper.maskメソッドに任意個数のElementオブジェクトを渡す事が出来ます.ですが,現状バグがあって正しく動作しません.

フィルターによる映像処理

SVGではfilter要素によるフィルター処理が行えますが,生のsvg要素を操作するのは意外に面倒です.Snap.svgではよく使うフィルターのテンプレートをSnap.filterオブジェクトとして多数収録しています.

Snap.svgでのフィルターの使い方

Snap.svgにおいてフィルターを使う場合は次の手順に則ります.

  1. Snap.filter.xxxメソッドを実行し,フィルターのソースコードを生成します.
  2. Paper.filterメソッドにこのソースコードを渡してfilterオブジェクトを取得します.
  3. 描画オブジェクトのfilter属性にfilterオブジェクトを渡します.

Paper.filter(filterStr)

Element.attr({filter: filterElement})

Paper.filterメソッドは渡されたfilter要素のソースコードからfilterオブジェクトを生成します.生成したfilterオブジェクトはfilter属性に渡すことが出来ます.

Snap.filterオブジェクトが提供するテンプレート

以下にSnap.filterオブジェクトが提供しているフィルターテンプレートを示します.

フィルターの一覧と動作例
名称意味合い呼び出し例動作例
Snap.filter.blur(x[,y])ぼかしSnap.filter.blur(4,1)
Snap.filter.shadow(dx,dy,[blur],[color],[opacity])Snap.filter.shadow(4,4,1,"gray",0.5)
Snap.filter.grayscale(amount)グレイスケール化
0〜1
Snap.filter.grayscale(1)
Snap.filter.sepia(amount)セピア化
0〜1
Snap.filter.sepia(1)
Snap.filter.saturate(amount)彩度変換
0〜1
Snap.filter.saturate(0.75)
Snap.filter.hueRotate(angle)色相回転
0〜360
Snap.filter.hueRotate(120)
Snap.filter.invert(amount)逆転
0〜1
Snap.filter.invert(1)
Snap.filter.brightness(amount)明度
0〜1
Snap.filter.brightness(0.2)
Snap.filter.contrast(amount)コントラスト
0〜
Snap.filter.contrast(2)

上記以外のフィルターの作成

より複雑なフィルターが必要な場合は,スクリプトに頼るのではなく,文書中に静的なfilter要素として定義しましょう.SVGフィルターはバグが混在しやすく,全てをスクリプトで構成した場合,問題の原因を切り分けるのが面倒です.

またフィルターをパラメータ化して再利用したい場合はSnap.svgでのコードを参考にすると良いでしょう. 以下にSnap.filter.blurメソッドの内容を示します.

', {
        def: def
    });
};]]>

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"を指定しています.

Element.node

Element.nodeプロパティにはElementオブジェクトが内包しているSVGElementオブジェクトが格納されています.やむを得ずSVGDOMオブジェクトのもつapiを直接操作したい場合や,他のライブラリと連携する場合に重宝するでしょう.

Snap.svgを使うからと言ってSVGDOMを直接触る事を良しとしないのは間違いです.SVGDOMそのものにも魅力的なapiはたくさん備わっています.例えばSVGTextContentElement.getComputedTextLengthメソッドでは文字列の描画幅を求めることが出来ます.このようにSnap.svgの世界だけでコードを記述するのは,SVGDOMが本来持っているパワフルさを捨てることと同じです.

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

要素の複製

要素の複製にはcloneメソッドを利用します.

Element.clone()

Element.cloneメソッドは元々の要素ツリーを複製し,直後に挿入します.つまり,元のグラフィックの真上に複製されたグラフィックが重なります.そのため,呼び出し方によっては意図しない動作に見える事があります.

要素の検索

Elementオブジェクトの中身を検索するにはselect/selectAllメソッドを利用します.

Element.select(selector)

Element.selectメソッドは引数で与えられたセレクタ文字列を元に要素を検索します.その結果,最初に見つかった要素に対するElementオブジェクトを返します.セレクタで見つかった全ての要素を取得するにはElement.selectAllメソッドを利用します.

Element.selectAll(selector)

Element.selectAllメソッドは引数で与えられたセレクタ文字列を元に要素を検索し,その結果をSetオブジェクトとして返します.

要素の挿入

DOMツリーに要素を挿入する際に利用可能なメソッドを示します.SVGにおいては何れもグラフィックの描画順を決定する上で役に立つものばかりです.メソッドチェーンを円滑に行うためにメソッドを実行した主体のElementオブジェクトが返されます

Element.append(element)

Element.appendメソッドは引数に与えたオブジェクトを子要素リストの末尾に追加します.

Element.add(element)

Element.addメソッドはElement.appendメソッドのエイリアスです.

Element.appendTo(element)

Element.appendToメソッドは引数に与えたオブジェクトの子要素リストの末尾にElementを追加します.

Element.prepend(element)

Element.prependメソッドは引数に与えたオブジェクトを子要素リストの先頭に追加します.

Element.prependTo(element)

Elemnt.prependToメソッドは引数に与えたオブジェクトの子要素リストの先頭にElementを追加します.

Element.before(element)

Element.beforeメソッドは引数に与えたオブジェクトをこの要素の前に挿入します.

Element.insertBefore(element)

Element.insertBeforeメソッドは引数に与えたオブジェクトの前にElementを挿入します.

Element.after(element)

Element.afterメソッドは引数に与えたオブジェクトをこの要素の後に挿入します.

Element.insertAfter(element)

Element.insertAfterメソッドは引数に与えたオブジェクトの前にElementを挿入します.

メソッドの分類

これらのメソッドを動作に応じて分類すると次のようになります.

メソッドの分類
対象を移動させる自身が移動する
末尾に追加append/addappendTo
先頭に追加prependprependTo
前に追加beforeinsertBofore
後に追加afterinsertAfter

要素の削除

要素を削除するにはremoveメソッドを使います.

Element.remove

Element.removeメソッドはsvg要素から対応する要素を取り除きます.その結果,見た目上グラフィックから図形が削除されたように見えます.

なお,Element.removeメソッドを実行したとしてもElementオブジェクトが何らかの変数に格納されている限り再利用できます.そのためこのメソッドは本来の目的の他に,Paperオブジェクト(svg要素)をDOMから切り離す目的で用いられます.

ツリー構造に関わるメソッド・プロパティ

要素関係についての機能はそれほど多くありません.

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画像内に挿入されていることがわかります.

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メソッドは子要素の内容を文字列として取得します.

Element.outerSVG()

Element.outerSVGメソッドはElement.toStringメソッドのエイリアスです.

Snap.svgでの描画内容をファイル出力する

この機能を用いてSnap.svgで描いたグラフィックを外部ファイルとして出力できます.

  1. Paper.toStringメソッドを実行し,基本となるSVGのソースコードを取得します.
  2. 既存の名前空間記述を削除し,新たに追加します(理由は後述します).
  3. encodeURIComponentメソッドを使ってURL形式に変換し,先頭にURIデータスキーム形式を表す文字列data:image/svg+xml;charset=utf-8,を追加します.
  4. この文字列を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を返します.

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が内部管理しているものですから,使わないで下さい.

図形の配置に関わる機能

Snap.svgでは図形の配置に関わる機能が提供されており,上手く活用することで図形の整列と言った作業を効率化することが出来ます.

バウンディングボックスとは

バウンディングボックスは各種オブジェクトを描画するために必要な最小の領域を指します.そのため,表示範囲や描画領域と呼ばれることもあります.Snap.svgではこのバウンディングボックスを得るためのメソッドが定義されています.

Element.getBBox()

Element.getBBoxメソッドはその図形要素に対するバウンディングボックスをbboxオブジェクトとして取得します.

次の例では描画したpath要素に対するバウンディングボックスを矩形として表示しています.

このようにバウンディングボックスを表示する場合はattrメソッドに直接bboxオブジェクトを渡すのが便利です.

bboxオブジェクトの内部構造

bboxオブジェクトで取得できる内容には次のものがあります.

bboxオブジェクトの構造
キー内容
x左上のx座標
y左上のy座標
x2右下のx座標
y2右下のy座標
w
width
h高さ
height高さ
pathbboxのpath文字列表現
vbviewBox形式
cx中心のx座標
cy中心のy座標
r0bbox全体を囲む円の半径
r1bboxに内接する円の半径
r2長い方に接する円の半径

bboxオブジェクトの表示方法

これを実際に表示してみましょう.なお,円を描画する場合はこのように半径に相当するr属性を書き換えてrectメソッドに渡すと便利です.

また,同様にバウンディングボックスに内接する楕円を描く場合は,このように幅と高さを比較し,r1,r2値をrx,ryに割り当てます.

図形を描画範囲全体に表示する

bboxオブジェクトのvb値をPaperオブジェクトのviewBoxに割り当てることで,図形の表示を描画範囲全体に広げることが出来ます.例えば,文字列の長さが判らないが,文字の描画をSVG領域にフィットさせたい場合に便利です.

また,特定の要素を選択した際に,その内容を拡大表示すると言った用途に応用できます.

特定の範囲を固定位置に合わせる(Snap)

図形を描画する際,Snap.snapToメソッドを用いると特定範囲の座標を特定の座標に合わせる事が出来ます.

Snap.snapTo(values, value, tolerance)

Snap.snapToメソッドはvalueの値が,valuesで指定した値とその差がtoleranceの範囲にあった場合にvaluesに指定した値を返します.toleranceを省略した場合10が与えられたものとして扱います.

このメソッドは専らドラッグイベントなどによる図形の移動時に,その位置を特定の座標に合わせると言った用途に用います.実際に例を見たほうがイメージしやすいでしょう.SVG上のカーソルが文字通り指定した位置に"Snap(噛み付く)"していることが判るでしょう.

パスの編集に関わるユーティリティー機能

SVGDOMにおけるパスの編集機能はお世辞にも使いやすいとは言えません.Snap.svgではこの機能を大幅に強化しているため,SVGによる表現力が劇的に向上します.

Snap.pathオブジェクトが提供する機能

Snap.pathオブジェクトはSVGにおけるパス図形に対して様々なユーティリティーを提供します.以下にその内容を列挙します.

Snap.pathオブジェクトの内容
メソッド名機能概要Element
bezierBBox3次ベジェ曲線に対するバウンディングボックスを取得します.
findDotsAtSegment3次ベジェ曲線をtの位置で分割します.
getBBoxパス文字列に対するバウンディングボックスを取得します.
getPointAtLengthパスの道のりに対する座標を求めます.
getSubpath部分パス文字列を取得します.
getTotalLengthパスの全長を求めます.
intersectionパスの交点を求めます.
isBBoxIntersectバウンディングボックスが共通部分をもつかを判定します.
isPointInside閉じたパス図形の範囲内に座標が存在するかを判定します.
isPointInsideBBoxバウンディングボックス内に座標が存在するかを判定します.
mapパス文字列に行列による変形を施します.
toAbsoluteパス文字列を絶対座標表記に変換します.
toCubicパスの内容を3次ベジェ曲線に変換します.
toRelativeパス文字列を相対座標表記に変換します.
get図形要素から同等のパス文字列を生成します.※非公開

バウンディングボックスに関わるもの

ここではバウンディングボックスに関わるメソッドについて説明します.

Snap.path.getBBox(pathString)

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幅を変えずに図形を変形したいと言った場合に用いると良いでしょう.

Snap.path.toRelative(pathData)

Snap.path.toRelativeメソッドは図形の内容を変化させずにパスデータを全て相対位置指定コマンドに置き換えます.

Snap.path.toAbsolute(pathData)

Snap.path.toAbsoluteメソッドは図形の内容を変化させずにパスデータを全て絶対位置指定コマンドに置き換えます.

Snap.path.toCubic(pathData)

Snap.path.toCubicメソッドは図形の内容を変化させずにパスデータを全て3次ベジェ曲線コマンドに置き換えます.

パス制御機能の応用

セクションの締めくくりとして,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})

例を示します.

なおこの操作は次のような用途に利用できますが,実際には余り使われないかも知れません.

  • 鏡像成分の抽出
    スケール値に負数が含まれている場合,そのグラフィックが反転していることになります.
  • グラフィックの一部固定化
    スケール値や回転値の逆数を求め,固定化したい内容(ストローク幅,傾き,サイズ等)に変形を施しておきます.こうすると全体での変形と個別の変形とが打ち消し合ってグラフィック内容の一部を固定化することが出来ます.

行列の複製

行列の内容は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を返します.

例えば次のようなコードが存在したとします.

                              <==元々の階層(このsvg要素のScreenCTMをSとする)
                  <==   Aの階層
                  <==  ABの階層
              <== ABCの階層
        
    
]]>

すると,transform状況の内容は次のようになります.(SVGDOMのgetCTM/getScreenCTMメソッドから返される値も合わせています)

このメソッドは図形の位置や大きさを制御する際に用います.次の例ではtransform変形が為されているcircle要素の中心を基準に円を描くサンプルです.

このように,図形に対してどのような変形が施されていたとしてもその座標を計算できます.どの行列を用いるかは図形をどの階層に挿入したいかによって変化します.例えば対象とする図形と同じ階層に配置するのであれば,localMatrix/stringを施せばよいでしょう.

その他のtransformに関わるユーティリティー

ここではその他のユーティリティー関数について示します.

Snap.parseTransformString(transformString)

Snap.parseTransformStringメソッドはSnap.svg形式のtransform文字列をtransform要素毎の配列に分解します.

gradientTransform,patternTransform属性への設定

Matrixオブジェクトはgradientオブジェクト,patternオブジェクトに対しても有効です.

gradientオブジェクトへの適用

gradientオブジェクトへはgradientTransform属性を用います.

patternオブジェクトへの適用

patternオブジェクトへはpatternTransform属性を用います.

色に関わるユーティリティー関数群

cssでは様々な形式の色指定が可能ですが,その相互変換が出来ませんでした.Snap.svgでは色に関わる様々なユーティリティー関数が定義されています.

サポートしている色の書式

Snap.svgがサポートしている色の書式を示します.

記述可能な色の書式
形式説明
color-nameHTML色名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の拡張です.

パーセント値/角度による値の指定

色の指定にパーセント値を混在することが出来ます.また,色相値には角度を指定することも出来ます

色相環の作成例

以下に色相の角度と色の関係を示します.

色書式の相互変換

先ほどの色書式の内容について,相互変換するためのメソッドが多数収録されています.処理の内容によって分類してみましょう.

  • 色情報オブジェクトを生成するもの
    color…色情報の全てを生成するもの
    getRGB…RGB情報のみを生成するもの
  • 特定の形式間の変換を行うもの
    hsb2rgb,hsl2rgb,rgb2hsb,rgb2hsl
  • hex形式専用…指定した内容をhex形式の文字列に変換する.アルファチャネル非対応.
    rgb,hsb,hsl
変換関数の相関
変換先
hexrgbhsbhsl
変換元 color name color/getRGB
hex -color
getRGB
color
getRGB
color
getRGB
rgb rgb-rgb2hsbrgb2hsl
hsb hsbhsb2rgb-color
getRGB
hsl hslhsl2rgbcolor
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属性を直接書き換えることで対処します.

データ操作に関わるユーティリティー関数群

データ型やデータの格納に関わるユーティリティー関数が定義されています.

データの保管

次のメソッドは要素に一時的に何らかのデータを保管しておきたい場合に利用します.なお,これは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)が取得されます.

判定処理

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によるアニメーションを強力にサポートします.

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にはアニメーション終了時に呼び出される関数を渡します.

アニメーション終了時に値を元に戻す

アニメーション終了時に値を元に戻す場合は,コールバック関数にその内容を記述します.

アニメーションを繰り返す

アニメーションを繰り返すのであれば,例えば次のようにアニメーション開始関数を定義し繰り返し実行されるようにします.

異なるアニメーションを同時実行する

また,異なるアニメーションを同時に実行することもできます.

アニメーションの逐次実行

異なるアニメーションを逐次的に実行する場合は,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つのイージング関数が定義されています.以下にその動作例を示します.

minaオブジェクト組み込みのイージング関数
メソッド動作例説明
mina.linear 線形(等速)
mina.easein 減速
mina.easeout 加速
mina.backin 一旦戻ってから加速
mina.backout 通りすぎてから戻る
mina.bounce バウンド
mina.elastic バネ弾力運動

イージング関数の変化量

イージング関数をグラフにすると次のようになります.横軸が時間経過(time),縦軸を変位割合(percentage)としています.

t=0 t=1 p=0 p=1

イージング関数の自作

minaオブジェクトで定義されているものの他にもイージング関数は自作できます.引数として区間[0〜1]の数値(経過時間)を受け,進捗値(通常0が初期位置,1が終了位置)を返すように関数を定義すると,イージング関数として機能します.下の例では経過時間を自乗する関数を使って放物運動を表現しています.

なおこのテクニックはアニメーションを設計する上で非常に重要です.工夫次第ではアニメーションの繰り返しや移動方向の制御に応用できます.

他のライブラリからイージング関数を読み込む

一般にイージング関数の構造はライブラリによらず似たようなものになります.従って他のライブラリのイージング関数を上手くラップすることでSnap.svgから呼び出すことが可能になります.下の例ではjQuery Easing Pluginのコードに手を加え,Snap.svg用のプラグイン化したものを使っています.

既存のイージング関数を利用する

既存のイージング関数を組み合わせることで,独自のイージング関数を定義することが出来ます.例えば,下のようにアニメーションの動きを逆方向とすることも可能です.

アニメーション状況の取得

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メソッドを実行します.

アニメーションの進捗状況を設定する

先ほどの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オブジェクトを渡すことでアニメーションを実行することが出来ます.

複数の要素にまたがるアニメーションを実行する

Element.animateメソッドは単一の要素に対するものでしたが,Set.animateメソッドを用いると複数の要素にまたがるアニメーションを実行出来ます.

複数の要素にまたがるアニメーションを実装する場合,素朴に考えると次のようになります.

もちろんこれで十分ですが,Set.animateメソッドで書き換えると次のようになります.

Set.animate(attr,dur,easing,callback/setting,...)

Set.animateメソッドは内部の要素に対してアニメーションを実行します.引数の内容はElement.animateメソッドと同じです.なお,callback関数は全てのアニメーションが完了した後に一度だけ呼び出されます.

要素ごとにパラメータを変更する

要素ごとに異なるアニメーションパラメータを指定する事も可能です.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.stopメソッドをアニメーションコールバック関数内で呼び出すと無限ループとなります. これは, anim.stopメソッドが自身のコールバック関数を呼び出すためです. 従って, コールバック関数内部から自分自身を停止する場合は, 何らかのフラグ値を定義しanim.stopメソッドの呼び出しを抑制する必要があります.

アニメーションの一時停止と再開

Snap.animateメソッドを使った場合,アニメーションを一時停止・再開することが出来ます.例を示します.

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オブジェクトを用いると,この「時間」とは別の基準(例えばスクロール量等)を基準にアニメーションを構成することができるのです.

下の例ではアニメーションの基準として別の要素の位置を使っています.

mina.time()

mina.timeメソッドは現在時刻を取得します.Snap.animate等のメソッド内で利用されています.

minaメソッドの応用

minaメソッドはアニメーションのみならず,setTimeout関数やrequestAnimationFrame関数に置き換わる関数の自動実行機構として利用できます.getメソッドの値を固定することでアニメーションの実行を終了せずに継続させることが出来ます.この場合,アニメーションの終了にはanimオブジェクトのstopメソッドを利用します.

下記はこの動作を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のセグメント構成を一致させると動作をイメージしやすくなります.

ドローツールを使ってアニメーション前後のパスデータを生成する場合は,頂点や制御点の数を変更せずに座標のみを変更するようにします.下の例ではアニメーションデータを外部の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.unhoverin/outの2関数を登録
Element.dragマウスドラッグElement.undragmove/start/endの3関数を登録
Element.touchstartタッチスタートElement.untouchstart
Element.touchmoveタッチムーブElement.untouchmove
Element.touchendタッチエンドElement.untouchend
Element.touchcancelタッチキャンセルElement.untouchcancel

ハンドラ関数に渡される内容

上記関数の引数にはイベント発生時に実行したいハンドラ関数を指定します.その際に指定したハンドラ関数には,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メソッドを用いることで簡単にドラッグ処理を実現できます.

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メソッドはタッチキャンセルイベントから処理を抹消します.

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メソッドを使って画面に表示しています.


]]>

Snap.loadメソッドがSVGファイルをロードできない

Snap.loadメソッドを用いてSVGファイルを読み込んだ際に上手く行かない場合があります.これは参照したSVGファイルにXML宣言/処理命令が含まれている事に起因します.例を示します.

これらはグラフィック的には全く同じ内容ですが,後者はSnap.loadメソッドに失敗します.

そのため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メソッドを実行します.目的に応じて使い分けるようにしましょう.

外部スタイルシートを参照していた場合

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値が重複しないように考慮する必要があるでしょう.

  • symbols.svgの中身
    
    
       
          
       	     
          
          
       	     
          
       
    ]]>
  • 図形を参照しているコード
    
       
       
    ]]>

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メソッドを用いるとこの中身を擬似的に操作可能です.以下に手順を示します.

  1. img要素のsrc属性をSnap.ajaxメソッドに渡してPaperオブジェクトを生成します.
  2. 得られたPaperオブジェクトを使ってグラフィックを操作します.
  3. PaperオブジェクトのtoStringメソッドを使ってSVGのソースコードを取得します.
  4. encodeURIComponentメソッドを使い,SVGのソースコードをURIdataスキーム形式に変換し,元のimg要素に設定します.

カスタムイベントとカスタム属性

Snap.svgでは独自のイベント処理機構を内蔵しています.これを用いると柔軟なイベント駆動プログラミングが行えます.また,それに伴うカスタム属性の作り方について解説します.

eveオブジェクトによるカスタムイベント

eveオブジェクトはSnap.svg独自のイベント駆動プログラミングフレームワークです.予めハンドラ関数を登録しておくと,カスタムイベントを発行した際にバインドしておいた処理を呼び出すことができます.基本的な考え方はDOMが提供しているイベント機構と同等ですが,uiイベントを前提としていないため,より広範な範囲で利用できます.

簡単な例を示します.ここではeve.onメソッドで「mylogging」イベントに対する処理を定義しておき,eveメソッドで「mylogging」イベントを発行しています.

上記からイベント発行部とハンドラ呼び出しとがeveオブジェクトを介して緩やかに結合している事がわかります.こうすることで処理の呼び出し元の変更を伴わずに複数のハンドラ関数の追加・削除や優先順位の指定が可能となるのです.

Snap.svgではこの機構をアニメーションやUIイベント処理などの様々な場面で利用しています.

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メソッドはハンドラ関数による戻り値を配列として返します.ですが,イベント駆動型のプログラミングでは,呼び出し元はハンドラ関数によってどのような処理が為されたかを識る術はありません(戻り値が存在しない場合もあります).従ってこの戻り値に依存したプログラムが必要であれば,ハンドラ関数の指定に条件を設けるなどの設計を行うべきです.

イベント優先度の設定

eve.onメソッドはfunctionオブジェクトを返します.これは単一のカスタムイベントに複数のハンドラ関数が結び付けられていた場合にその優先順(zIndex値)を指定するもので,値の小さいものから順に実行されます.例を示します.通常は登録順に実行されるはずの処理の順を逆にしています.

なおこのイベントの発生順位は下で示すイベント階層を跨いで行われます.ハンドラ関数の処理順が重要な場合は全てにzIndex値を指定しましょう.

イベントの階層化

カスタムイベントを登録・呼び出す際,イベント名の区切り子として「.」もしくは「/」を指定するとイベントを階層化することができます.階層化されたイベントは左側(親)のイベントから順にマッチングされ,合致したものから順に実行されます.また,イベント登録時はワイルドカード「*」での指定が可能です.DOMイベントにおけるバブリング機構のような処理を行えます.

eve.once(name, handler)

eve.onceは一度だけ有効なイベントを登録します.eve.onで登録した場合と異なり,一度実行されると自動的にハンドラ関数の登録が破棄されます.アニメーションの実行処理などで利用されています.

eve.off(name, handler)

eve.offメソッドはonメソッドで登録したイベントを削除します.nameだけを指定すると,そのnameに登録されているものすべてが削除されます.

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を使いこなすのであれば是非マスターしておきたいテクニックです.

カスタム属性の導入場面

カスタム属性が有効なケースとしては次のような場合が挙げられます.

  • path要素に意味付けを行い,新しい図形オブジェクトを定義する場合
    よく知られた図形であれば,大抵は数個のパラメータを与えることで図形の形状が確定します.従って,このパラメータをカスタム属性として定義し,その内容からパス図形を描くようにすると図形の再利用が可能になります
  • g要素に意味付けを行い,複数のオブジェクトをまとめた新しい図形オブジェクトを定義する場合
    上の例と同様ですが,カスタム属性を定義する対象をg要素とし,カスタム属性設定時に中身を書き換えるようにします.こうすることで複数の図形からなるグループをあたかも単一のオブジェクトとして扱えます.
  • 複雑なオブジェクト生成を要する場合
    図形要素のみならず,グラデーション等の複雑な要素生成が必要となるケースにおいては,カスタム属性を使ってその処理をひとまとめにしておくと後々のコードの再利用が楽になります.
  • アニメーションの制御を行う
    複数の属性にまたがるアニメーションを作る場合,アニメーションの進捗値に対応するカスタム属性を定義し,その内容に沿って図形のプロパティを書き換えるようにします.こうすることで単一のカスタム属性をアニメーション化するだけで高度な動きを実現することが可能となります.

このようにSVG部品を定義してライブラリ化する際に非常に有効です.

カスタム属性の定義

カスタム属性を定義するには次の手順を踏みます.

  • eve.oneメソッドを使い,「snap.util.attr.[カスタム属性名]」イベントに対する処理を登録する.
  • 必要に応じeve.onメソッドを使い,「snap.util.getattr.[カスタム属性名]」イベントに対する処理を登録する.

カスタム属性の動作原理

Element.attrメソッドはSVGの要素に対して属性値を書き込んでいますが,その仕組みは次のとおりです.

  1. 事前にeve.onメソッドを使って共通イベント「snap.util.attr/getattr」が定義されています.
    このイベントには属性名と値からsetAttribute/getAttributeメソッドを実行する処理が登録されています.
  2. Element.attrメソッドが呼び出されると,引数の内容から「snap.util.attr/getattr.[属性名]」イベントを発行します.
    すると先ほど登録しておいた一階層上のイベント「snap.util.attr」が発生し,登録しておいたメソッドが実行されます.

その結果,domが書き換えられるのです.

このことから,「snap.util.attr.[カスタム属性名]」イベントに対する処理を登録することで,Element.attrメソッドから独自の処理を呼び出せるようになります.この時eve.onメソッドに渡す関数には,Element.attrメソッドによって,this変数としてElementオブジェクトが,引数としてattrメソッドで渡したパラメータが設定されます.例を示します.ここではmyAttribute属性を定義しています.

このようにdomへの書き込みを行いつつ,独自の処理を実行することが出来ました.

イベント処理の優先順位を意識する

カスタム属性の定義はこれだけでも十分ですが,より高度な処理を行いたい場合はその優先順について意識する必要があります.Element.attrメソッドによって実行される処理の順番は次のとおりです.

  1. 優先度:-1 「snap.util.attr/getattr.[属性名]」イベント
    特殊なパラメータ設定が必要な場合に対する処理.mask,transform等
  2. 優先度: 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,Paper,Element以外のオブジェクトを拡張する

先ほどの例から判るようにSnap.pluginメソッドが主眼においているのはSnap,Paper,Elementオブジェクトです.従ってSet/Fragment/Animationオブジェクトを拡張するには少し細工をする必要があります.例えばSetオブジェクトを拡張する場合,一旦Setオブジェクトのインスタンスを取得し,そのコンストラクタ関数のprototypeオブジェクトを見つけます.以降はElementオブジェクト等と同じようにprototypeオブジェクトに機能を追加していくだけです.例を示します.

この通り,実はSnap.svgを拡張するのにSnap.pluginメソッドを使う必要は無いのですが,無闇に拡張を繰り返すとコードの管理が面倒となるため気をつけましょう.

既存メソッド拡張の一般形

既存のメソッドを拡張をする場合,よく用いられる方法として元々のファンクションをラップして置き換えるものがあります.

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メソッドからアクセス可能とする.

以下に実際のコードを示します.いずれも難解な部分は無く,すっきりと記述できていることがわかります.

パフォーマンスチューニング

SVGによるグラフィック描画は美しい結果が得られる反面,マシンに対しては負荷が高いものです.Snap.svgにおいてもSVGのもつパフォーマンス特性について念頭に入れておくべきでしょう.

SVGにおけるパフォーマンスチューニングの原則

SVGではグラフィック描画を部品に分けて管理しています.従ってこの部品の数が増えていく,もしくは複雑な部品を追加していく毎にSVGの描画パフォーマンスは低下していきます.これはSnap.svgを使った場合も同じで,特にスクリプトでグラフィック部品を自動生成している場合,いつの間にか許容できないほどのパフォーマンス劣化を招く事があります.

この問題を回避するには,つぎの点について見直しましょう.

  • ノード数を極力減らす
    HTMLと同様にSVGにおいてもDOMツリーの肥大はパフォーマンスの劣化を引き起こします.特にスクリプトを使ったSVGグラフィック構築ではシンプルなロジック程,細切れの要素が作られがちです.同じようなオブジェクトが多数存在している場合は複合パスによる単一の図形に結合出来ないか検討します.
  • フィルター処理を見直す
    フィルター処理はピクセル操作と同等であり,大きな図形要素にフィルターを掛けるとそれだけでシステムに負荷がかかります.また,SVGの現仕様ではフィルター処理が行われるタイミングを制御することが出来ない等の問題があります.そのため,極力フィルター処理は避け,同じ結果をもたらす別のやり方はないか検討しましょう.さもなくばフィルター処理を諦めることも視野に入れるべきです.
  • パスの複雑度を下げる
    SVGにおいては仕様上パスの精度を際限無く上げることが出来ますが,web用途ではスクリーンに表示した際に違和感がなければ十分と言えます.従って,(地図データから変換したような)生のパスデータを直接使うのでなく,ある程度頂点を減らしたり直線化したものを用いましょう.また,座標の精度桁数を削り,パスデータの通信量を下げることも体感速度を向上させます.
  • Paperオブジェクトはremoveしてから使う
    Snap.svgではPaperオブジェクトを取得する際に暗黙的にDOMツリーにsvg要素を挿入します.従ってそのまま描画処理を実行すると,その都度DOMツリーを更新することになってしまいます.このようなDOMツリーの逐一更新処理は無用なグラフィックの再描画を促すため,処理速度に著しい影響を与えます.従ってPaperオブジェクトを取得した場合は,とりあえずremoveメソッドを実行し生成したsvg要素をDOMツリーから切り離します.その上でグラフィックを描いていき,最後にDOMツリーに書き戻すようにします.
    なお,メソッドによってはDOMツリーに存在しないとエラーとなるものもありますから,一概にこうすべきというわけではありません.
  • 複数の要素を移動する場合はFragmentオブジェクトを使う
    先程と同様の理由から,複数の要素を逐一移動させるとその都度グラフィックの再描画が発生します.そのためFragmentオブジェクトを利用し,DOMツリーの更新回数を減らすことでパフォーマンスを向上することが出来ます.
  • Elementオブジェクトの生成・廃棄に気をつける
    似たようなElementオブジェクトを複数生成する場合,都度Paper.xxxメソッドを実行するのではなく,Element.cloneメソッドを用いて要素を複製するようにしましょう.また,一度生成したElementオブジェクトは出来る限り再利用しましょう.多量のElementオブジェクトの生成・廃棄を繰り返すと,Snap.svgのキャッシュ機構がメモリリークを発生します.そうでなくともwebブラウザのガベージコレクション処理(メモリの開放)を誘発し,場合により(フリーズ・クラッシュ等の)致命的な問題を引き起こします.必要最低限のElementオブジェクトの生成を心がけましょう.
  • アニメーション処理を減らす
    動きのあるwebページは一見華やかですが,過剰なものは却ってユーザビリティを妨げます.無駄なアニメーションは処理速度の面からも思い切って削除してしまいましょう.
  • イベントでの処理量を減らす
    繰り返し発生しうるイベントに対しては,出来る限り事前処理を施したりオブジェクトを再利用したりし,1イベント発生に対する処理量を少なくするようにしましょう.また,より低頻度で発生するイベントで代用できないか検討しましょう.(mousemoveはmouseoverで代用できないか等)
  • 余計な検索コストを削減する
    Paper.selectメソッド等のDOMを検索するメソッドはなるべく使うべきでありません.従って,繰り返し利用する可能性のあるオブジェクトはローカル変数に格納しておき,余計なDOM走査コストを省きましょう.また,検索する範囲を限定する,セレクタ文字列に指定する検索条件をより具体的なものとするといった工夫も有効です.
  • Snap.svgのapiを使わない
    DOMを直接操作するようにすればそれだけ処理が少なくて済みます.また標準DOMのapiよりもSVGDOMが提供しているものの方が動作が軽快です.例えば下のようなattrメソッドを使うコードは次のように書き換えることでパフォーマンスの向上が図れます.
    circle.attr({cx: 100, fill:"red"});
    //↓標準的な書き方で書き直す
    circle.setAttribute("cx", 100);
    circle.setAttribute("fill", "red");
    //↓更にSVGDOMで書き直す
    circle.node.cx.baseVal.value = 100;
    circle.node.style.fill = "red";
    一回あたりの処理の軽減量は微々たるものですが,アニメーション等で処理が繰り返される場合は無視出来ない結果となります.
  • その他のHTMLDOMでのパフォーマンスチューニングテクニックを利用する
    SVGDOMは技術的にHTMLDOMとほとんど変わりありませんから,HTMLDOMでのパフォーマンスチューニングテクニックをそのままSVGDOMに適用することが出来ます.

とは言え,パフォーマンスを極限まで求めるとなるとそれはSnap.svgを使わないという結論になってしまいます.従って,費用(コード量増加・可読性低下)対効果(速度向上)を鑑みてバランスの良いチューニングを行いましょう.細かい部分については必要に応じて実行するかどうかを検討しましょう.

Raphaël.jsとの比較

最後にSnap.svgの元となったjavascriptライブラリ・Raphaël.jsとの違いについて見てみましょう.

Raphaël.jsとは

Raphaël.jsはSnap.svgと同じ作者によるjavascriptライブラリで,利用することでレガシーieを含む広範な環境でベクタ画像を描画可能となります.

レガシーie環境ではVMLと呼ばれるSVGと同等のベクタグラフィック描画機構が提供されています.Raphaël.jsは動作環境に応じてグラフィックをこのVMLもしくはSVGで描画するため,利用者側はそれらの仕様の違いを意識せず単一のapiを操作するだけで済むのです.しかしその代償として利用できる機能の範囲がSVGとVMLの共通部分に限られてしまいます.

かつてRaphaël.jsといえば,そのサポート範囲の広さからベクタグラフィックを描く際のデファクトスタンダードと言うべき存在でしたが,VML環境の衰退とSVGの隆盛に伴いその存在意義が曖昧になりつつあります.とは言え,長年にわたって使い続けられたことによる安定性や,機能が制限されている分,かえって判りやすいと言ったことから現在でも愛用されています.また,VMLを扱える数少ないライブラリですから,ieにレガシーie互換モードが存在する限りその価値は変わりません.

コードの例を示します.

このようにSnap.svgでのコードに極めて近い事がわかります.

Raphaël.jsとSnap.svgとの違い

兄弟とも言えるこれらのライブラリですが,Raphaël.jsでの経験がSnap.svgにて生かされていることから,多くの部分において仕様の改善もしくは変更が発生しています.以下にその変更箇所について簡単にまとめました.

機能Raphaël.jsSnap.svg
描かれるグラフィックの種類SVG/VMLSVG
環境定義
描画先の選定,サイズ,viewBoxの指定
専用メソッドによるattrメソッドによる
指定可能な属性・プロパティRaphaël.jsの指定範囲SVG仕様全般
色の自動出力getColorメソッドなし(自作する必要あり)
グラデーションの指定Raphaël.js記法によるSnap.svg記法による(互換性なし)
図形要素の移動(上に移動など)専用メソッドとして提供DOMの機能を利用
画像効果影の指定,ぼかし,矩形クリップSVGの範囲で利用可能
transform文字列Raphaël.js記法(Snap.svg互換)Snap.svg記法/SVG記法
stroke-dasharrayプロパティRaphaël.js記法SVG記法
マーカー指定Raphaël.js記法SVGによる指定
指定座標における図形の抽出専用メソッドとして提供なし(自作する必要あり)
アニメーション機構Raphaël.jsの独自実装minaによる実装(互換性なし)
ドラッグオーバー機構Raphaël.jsの独自実装なし(自作する必要あり)
フォントのロード機能Cufon形式で可能なし(自作する必要あり)
要素操作に関わる機能専用メソッドとして実装DOMの機能を利用
プラグイン機構jQueryライクな機構Snap.svgの独自機構
ベクタデータの流し込みjson形式での挿入SVGファイルとして挿入

逆に次の機能はRaphaël.jsからほぼそのまま移行されています.

  • Matrixオブジェクトによる行列計算
  • パス文字列の制御機能

Raphaël.jsからSnap.svgに移行すべきか

Raphaël.js前提のプロジェクトが既に構築済みの場合,無理にSnap.svgに移行する必要はありません.最新のブラウザ環境においても(SVGのサポートが続く限り)動作するからです.無理にSnap.svgに移行するのは細かい仕様の差異があることからも賢明とは言えません.また,レガシーie環境で動作しているシステムに新たにベクタグラフィックを描く機能を追加するのであれば,現状Raphaël.js以外の選択肢は存在しません.

これからプロジェクトを立ち上げる場合,レガシーIEを動作環境に含めるかどうかでRaphaël.jsを採るかSnap.svgを採るかを判断して下さい.Raphaël.jsでの開発経験がある場合は,それほど違和感なくSnap.svgに移行出来るはずです.

なお開発の注力は既にSnap.svgに移っており,今後Raphaël.jsの機能向上はそれほど期待できません.特にこだわりが無い限り,Snap.svgをお勧めします.

補足)Cufon形式のフォントデータをSnap.svgから利用する

Cufon形式のフォントデータ資産をそのままSnap.svgを用いて再利用することは出来ません.出来る限り他の形式のフォントを利用してください.とは言え,元となるフォントデータが存在しない場合やRaphaël.jsからの移行を検討している場合を想定し,Snap.svgにおいてCufonフォントを利用可能とするプラグインを用意しました.