19・Raphaëlを用いたベクタグラフィック
svgの持つ標準apiを使うだけでもそれなりの処理を行うことができるが,Raphaël—JavaScript Libraryを用いることでsvgが動作しないieにおいても擬似的にsvgを描画できるようになる.また,svgを描画する目的であっても非常に優秀である.ここではRaphaëlの使い方について紹介する.なお,執筆時のバージョンは2.1.0のため,最新の情報については配布サイトを参照されたい.※このページではRaphaëlの動作サンプルを掲載しています.記述されているコードを実際に動作させているため,若干ロードが遅いのでご注意下さい.なお,ie8ではvmlでの動作が確認できます.
※ie環境を対象としないのであれば,合わせてSnap.svgを検討してみましょう.svgのみをターゲットとしていることからより高度な処理が可能となります.
Raphaëlの機能
Raphaëlはwebブラウザでベクタグラフィックを記述する為のjavascriptライブラリであり,html文書内部での利用を想定している.MIT Licenseで配布されているため,ライセンス的にも利用しやすい.ここでsvgと限定していないのは,ベクタ画像を出力するためにブラウザにより出力形式をsvgとvml(internet explorerの場合)とに自動的に振り替えているためである.この動作のためプログラマーはブラウザの種類を意識すること無く,単一のインターフェースを操作するだけで済む.従って,Raphaëlがサポートする機能は基本的にsvgとvmlの共通部分をベースとしているが,独自に実装している機能は驚くほど強力である.
- 基本図形の描画
- グラデーションの設定
- 図形要素の管理
- ラスタ画像の挿入
- テキストの挿入
- アニメーションの実行
- イベントの捕捉
以下はRaphaëlが独自に提供している機能だ.
- 簡略化されたマーカー・線の装飾
- 強力な図形変換処理(ベジェ曲線の変換,分割,描画領域の取得など)
- 強力な色の変換処理(rgb,hsb,hsl色空間のサポート)
- Cufón形式のフォント描画
- eveによる柔軟なカスタムイベント処理
- 汎用的なメソッドの提供
svgの持つクリップ・マスク操作,フィルター操作等の画像の合成に関わるものは簡易的な物を除いて利用できないが,それを補って余りある機能を有しているのだ.また,一般に冗長となりがちなsvgdom操作ではあるが,Raphaëlを用いることでjsonを用いた図形の一括指定が出来るようになるなど,javascriptの持つ強力な構文機構を利用して簡潔にコード記述できるようになる.従って,Raphaëlをsvgを描画する為のライブラリとして利用することも出来る.
注意すべき点としてはRaphaëlが既存のsvg要素の内容にアクセスする機能は持っていない点である.従って,illastrator等で描画した内容をそのままRaphaëlで読み込むと言った用途には向かず,もっぱらグラフやダイアグラムを動的に作るといった場面で威力を発揮することとなるだろう.なお,ユースケース的には十分に有り得るため,有志によりraphael-svg-importと言ったプラグインや,SVG2Raphaelといったものが提供されている.本項でもxslを使った簡単な変換ツールを紹介する.
Raphaëlの構成
Raphaëlが提供しているオブジェクトは次の8つである.数が少ないので,それほど複雑ではない.
- Raphaelオブジェクト
Raphaëlを構成する最も基本となるオブジェクトである.ベクタグラフィックの描画領域の定義の他,各種ユーティリティ機能を提供する.
- Paperオブジェクト
ベクタグラフィックを描画する領域を表すオブジェクトであり,svgにおけるsvg要素に相当する.図形を描画するための機能を提供する.
- Elementオブジェクト
基本図形を表すオブジェクトであり,色や,見た目の制御を行う為のapiを提供する.
- Setオブジェクト
基本図形のグループ化を表すオブジェクトであり,スタイル属性の一括設定や,様々な配列操作を提供する.
- Animateオブジェクト
図形に対するアニメーションを定義するオブジェクト.
- Matrixオブジェクト
transform処理を行う際の行列演算をサポートする機能を提供する.
- eveオブジェクト
カスタムイベントを定義・実行するライブラリ.
- fontオブジェクト
Cufón形式のフォントデータを格納するオブジェクト.
オブジェクトの相関を表すと次のようになる.
Raphaëlによるグラフィックの描画手順
Raphaëlを利用するには他のjavascriptライブラリと同様にhtmlのheader要素内部で
「<script type="text/javascript" src="raphael-min.js"></script>
」と記述する.jQueryとの併用も可能だが,組み合わせて利用するケースは殆ど無いと思われる.
グラフィックを描画する手順は次のとおりである.
- グラフィックを描画したい部分をdiv要素で定義しておく.
- このdiv要素を元にpaperオブジェクトを取得する.
- paperオブジェクトを元に図形オブジェクトを生成する.
- 図形オブジェクトに対してスタイルを設定する.
下の例はidにcanvas1を持つdiv要素にグラフィックを描いた例である.
グラフィック描画メソッドとしてsvgが選択された場合,このdiv要素内部にsvg要素が配置される.一方vmlが選択された場合,vmlは明確なコンテナ要素を持たないため,div要素に直接vmlの各種要素が配置される.cssでの見た目を調整する場合はsvg要素にはスタイルを設定しないほうが無難であろう.なお,div要素を指定せず,body要素における絶対位置で指定する方法もある.また,Raphaelにファンクションオブジェクトを登録することで,jQueryと同様にdomの解析が完了したタイミングで処理を実行することが出来る.
- Raphael
- document.ready時に処理を実行する.Raphael(func)
- Paperオブジェクトを生成する(div要素に描画する).Raphael(targetDivId, width, height)※HTMLDivElementを渡しても良い.
- Paperオブジェクトを生成する(body要素に直接描画する).Raphael(x, y, width, height)
以下,Raphaëlの使い方についてひと通り解説するが,いかんせん統一された情報に乏しく,公式配布サイトのapi表も御世辞にも親切とは言えない.従って筆者独自の解釈が多分に含まれており,実際の設計思想と異なる場合があることを予めお詫びする.なお,公式サイトにおいては具体的な応用例について多数公開されており,ある程度まではその思想を汲むことは可能である.なお,ライブラリの不具合により正常に動作しないものもあるので注意のこと.
カンバスサイズ・ビューボックスの設定
Paperオブジェクトにはベクタグラフィックスの表示範囲を定義する関数setViewBoxが定義されている.これはsvgにおけるviewBoxに相当する機能である.
- Paper.setViewBox
- グラフィックの表示範囲の指定.
setViewBox(x,y,width,height,fit)(falseの場合左上を基準とする.trueの場合引き伸ばされる.)※古いfirefoxでは正しく動作しない
また,setSizeメソッドでカンバスのサイズを後から変更することも出来る.※下の例ではsvg要素におけるスタイル値が有効となっているので,見た目大きさが変わっていません.
- Paper.setSize
- 描画カンバスの大きさを変更する.setSize(width,height)
Raphael.setWindowメソッドを用いるとフレームを跨いだ描画も可能だ.iframeのもつcontentWindowオブジェクトを渡すことで,iframe内のdiv要素に図形を描画することが出来る.
- Raphael.setWindow
- ベクタグラフィックの描画対象をiframeにする.setWindow(iframe.contentWindow)
基本図形の描画
Paperオブジェクトには各種図形を生成するメソッドが提供されている.
- 図形オブジェクトを生成するメソッド
- 実行するとElementオブジェクトを生成する.
- Paper.circle
- 円を描画する.circle(x,y,r)
- Paper.ellipse
- 楕円を描画する.ellipse(x,y,rx,ry)
- Paper.image
- ラスタ画像を描画する.image(src,x,y,width,height)
- Paper.path
- パスを描画する.path([pathString:d操作の内容])
- Paper.rect
- 矩形を描画する.rect(x,y,width,height)
- Paper.text
- 文字列を描画する(初期設定:中央揃え).text(x,y,text)
生成した図形オブジェクト(Elementオブジェクト)に,attrメソッドで属性を設定していく.引数としてjson形式の設定情報を渡すこともできるが,jQueryと異なりcamel形式での指定はできない.つまり「stroke-width」を設定する場合は,キー文字列として「strokeWidth」を使用することは出来ず,二重引用符で囲った"stroke-width"を指定する必要がある.図形オブジェクトに設定可能な属性は次の通りである.図形要素によって有効な属性が異なる.またRaphaël独自の記述法が存在するものは後ほど解説する.
- Element.attr
- 属性値の設定/取得.attr([属性値名],[設定値])もしくはattr(json)で値の設定.attr([属性値名])で値の取得.
【図形の描画基準座標に関わる属性】※†付きはRaphaël独自の記述法で行うもの.
- x,y
- 図形の描画基準座標.テキストの描画基準座標.
- width
- 図形の幅.
- height
- 高さ.
- cx,cy
- 円・楕円の中心.
- r
- 円の半径及び矩形の四隅の丸め半径.
- rx,ry
- 楕円の半径.矩形の四隅の丸めには対応していない.
【パス専用の属性】
- path†
- パスを定義する操作.svgにおけるpath要素のd操作に相当する.
- arrow-end†
- パスの終点マーカー.[スタイル]-[幅(narrow/midium/wide)]-[長さ(short/midium/long)]
- arrow-start†
- パスの始点マーカー.[スタイル]-[幅(narrow/midium/wide)]-[長さ(short/midium/long)]
【塗りつぶしに関わる属性】
- fill†
- 塗りつぶしの色.
- fill-opacity
- 塗りつぶしの不透明度.(1で不透明,0で透明)
【線の描画に関わる属性】
- stroke†
- 線の色.
- stroke-dasharray†
- 破線の設定.独自形式での指定.例「- 」
- stroke-linecap
- 線の端点のスタイル.(butt無し|round丸め|square四角)
- stroke-linejoin
- 線の頂点のスタイル.(miter角|round丸め|bevel面取り)
- stroke-miterlimit
- 線の頂点の尖りの上限値.
- stroke-opacity
- 線の不透明度.(1で不透明,0で透明)
- stroke-width
- 線の太さ.
【テキストの描画に関わる属性】
- text†
- テキストの内容.\nで改行も可能.
- text-anchor
- テキスト描画の基準.
- font
- フォントの設定.
- font-family
- フォントファミリーの設定.
- font-size
- フォントの大きさ.
- font-weight
- フォントの太さ.
【図形の描画に関わる属性】
- blur
- 図形のぼかし幅.
- clip-rect†
- 図形の描画を制限する範囲.x,y,width,height
- opacity
- 図形の不透明度.(1で不透明,0で透明)
- transform†
- 図形の変形.独自記述.
【リンクに関わる属性とその他の属性】
- cursor
- カーソルの指定.
- href
- 図形クリックした際のリンク先.
- src
- ラスタ画像の参照先url.
- target
- リンクの表示先設定.(_top|_blank|[name])
- title
- 図形のタイトル.
- Element.type
- 図形オブジェクトのタイプ.circle,ellipse,image,path,rect,textの何れかが設定される.
図形の一括指定とSetオブジェクト
図形の描画,プロパティの設定はaddメソッドで簡略化することが出来る.
- Paper.add
- 図形を一括描画する.Setオブジェクトを返す.add([json notation]).
addメソッドを実行すると,その処理結果としてSetオブジェクトが返される.このオブジェクトに施した操作は,内部の画像要素の全てに適用される.Setオブジェクトを生成する方法としては,Paper.setメソッドを実行するPaper.addメソッドを用いるPaper.setStartメソッドを実行した後で,Paper.setFinishメソッドを実行するの何れかを行う.Setオブジェクトは一見svgのg要素によるグループ化に似ているものの,あくまでスクリプト処理の簡略化を目的とした機構である.Raphaëlが生成するdomは,全ての図形要素が同じ階層に存在するフラットな構造をとる.
- Paper.set
- Setオブジェクトを生成する.set().引数に図形オブジェクトを渡すことも可能.
- Paper.setStart
- Setオブジェクトの開始を宣言する.setStart().
- Paper.setFinish
- Setオブジェクトを取得する.setStartメソッド以降,Paperオブジェクトで描画した図形要素のリストを取得する.setFinish().
- Set.attr
- Set内部の図形要素に一括で属性値を設定する.
- Set.type
- 文字列「Set」が設定されている.
Setオブジェクトのもつメソッド
Setオブジェクトにはその他リスト操作に関わるメソッドが提供されている.
- Set.clear
- 中身を空にする.
- Set.exclude
- 指定した図形オブジェクトを削除する.
- Set.forEach
- Setオブジェクトに属する全ての図形オブジェクトに処理を行う.forEach(callback,thisArg).callback関数内でfalseを返すとループ処理が終了する.thisArgはcallback関数内部においてthis変数から参照できる.
- Set.pop
- 図形オブジェクトをポップする.
- Set.push
- 図形オブジェクトを追加する.
- Set.splice
- 図形オブジェクトを入れ替える.splice(index,count,[挿入する図形オブジェクト]).
- Set.items
- 内部の図形オブジェクトの配列.
- Set.length
- 内部の図形オブジェクトの数.
- Set.clone
- Setオブジェクトを複製する.
forEachメソッドの例を示す.
同様の機能はPaperオブジェクトにも定義されている.
- Paper.forEach
- Paperオブジェクトに属する全ての図形オブジェクトに処理を行う.
色の指定方法
Raphaëlではhtml定義色やrgb関数による指定の他,様々な方法で色を設定することが出来るため,柔軟な色の指定が可能となっている.色相は360°に対する割合で,彩度・明度・輝度は0〜1の割合で指定する.色相角に対応しているため,やや力技ではあるが,円環状のグラデーションを実現できる.
- color name
- htmlで定められている色名称
- #fff/#ffffff
- 16進数指定
- rgb(,,)
- rgb形式
- rgba(,,,)
- rgba形式
- hsb(,,)
- hsb色空間(色相Hue・彩度Saturation・明度Valueでの指定)
- hsba(,,,)
- hsb色空間(色相Hue・彩度Saturation・明度Value+不透明度Alphaでの指定)
- hsl(,,)
- hsl色空間(色相Hue・彩度Saturation・輝度Luminanceでの指定)
- hsla(,,,)
- hsl色空間(色相Hue・彩度Saturation・輝度Luminance+不透明度Alphaでの指定)
色の相互変換
Raphaelオブジェクトではこれらの色空間の記述形式を相互に変換するメソッドを提供している.
- Raphael.getRGB
- 色の名称/記法からrgb色空間の色{r,g,b,hex,error}に変換する.getRGB(color)
- Raphael.hsb
- hsb色空間の色を16進表記の色に変換する.hsb(h,s,b)
- Raphael.hsb2rgb
- hsb色空間の色をrgb色空間での値{r,g,b,hex}に変換する.hsb2rgb(h,s,b)
- Raphael.hsl
- hsl色空間の色を16進表記の色に変換する.hsl(h,s,l)
- Raphael.hsl2rgb
- hsl色空間の色をrgb色空間での値{r,g,b,hex}に変換する.hsl2rgb(h,s,l)
- Raphael.rgb
- rgb色空間の色を16進表記の色に変換する.rgb(r,g,b)
- Raphael.rgb2hsb
- rgb色空間の色をhsb色空間での値{h,s,b}に変換する.rgb2hsb(r,g,b)
- Raphael.rgb2hsl
- rgb色空間の色をhsl色空間での値{h,s,l}に変換する.rgb2hsl(r,g,b)
なお,fillメソッド等で空文字列を指定した場合,無色(none)として扱われる点に注意が必要だ.
色の自動生成
また,階段状の色の変化を表現するためにgetColorメソッドが提供されている.グラフなどで使うと美しいグラデーションを実現できるだろう.
- Raphael.getColor
- 赤を基準に次の色を取得する.引数には明るさを0〜1の範囲で渡す.14×6(84)回の操作で元の色に戻る.getColor(brightness)
- Raphael.getColor.reset
- 色のリセットを行う.
グラデーションの設定
図形オブジェクトのfill属性にグラデーションを設定することが出来る.css3におけるグラデーションの指定に近く,シンプルに実装できる.
記述法:[角度]-[color-stop]-[color-stop]
※color-stop値は[color]:[offset%]
とする.
グラデーションを設定したオブジェクトに対しopacity属性を設定すると,不透明度にグラデーションが掛かる(正常な動作なのかvmlの制限なのか不明).
放射状グラデーション
circle及びellipseに限られるが,放射型グラデーションも設定できる.その際,焦点を設定することも可能である.
記述法:r ([焦点の位置0〜1x],[焦点の位置0〜1y])[color-stop]-[color-stop]
図形の表示に関わる操作
基本図形には色や見た目の他に,描画に関わる様々な機能が提供されている.
- Element.toFront
- 前面に移動
- Element.toBack
- 背面に移動
- Element.hide
- 図形要素を隠す
- Element.show
- 図形要素を表示する
- Element.glow
- 図形要素の描画に効果をつける.影のような効果をつけることが出来る.
- width
- 広がりの幅
- fill
- 塗りつぶすかどうか
- opacity
- 透明度
- offsetx
- 横方向のずらす幅
- offsety
- 縦方向のずらす幅
- color
- 色
前にも示した通り,Setオブジェクトは個々の図形に対するスタイルの指定を簡略化するための機構であるため,影の処理が図形毎に行われている点に注意する.つまり,Raphaëlにおいてはgrowメソッドを用いて複数の図形を組み合わせたもの全体に影を付けると言ったことができない.影専用の図形を用意して元となる画像の背後に配置すると言った工夫が必要となる.
この他、図形の描画に関わる属性としてblur(ぼかし)とclip-rect(描画範囲の制限)がある.
図形の変形
基本図形に対する変形処理には様々なサポート機能が提供されている.※svgにおけるskew(せん断)に相当する機能は存在しない.
- Element.scale
- 図形要素を拡大・縮小する.scale(sx,sy,cx,cy)※中心は省略可能
- Element.rotate
- 図形要素を回転する.rotate(deg,cx,cy)※中心は省略可能
- Element.translate
- 図形要素を平行移動する.translate(dx,dy)
- Element.transform
- 図形要素を変形する.
- t操作
- translate:平行移動(t dx,dy)
- r操作
- rotate:回転(r deg,cx,cy)
- s操作
- scale:拡大・縮小(s sx,sy,cx,cy)
- m操作
- matrix:マトリクス変換(m a,b,c,d,e,f)
- Raphael.parseTransformString
- transform文字列を変形操作の配列に分解する.
transformメソッドは非常に高機能で,キーワード「...」(ピリオド×3)を指定することで変形の追加を行うことが出来る.
transformメソッドの補助を行う機能としてMatrixオブジェクトが提供されている.これは図形の変形を(アフィン)行列で表したものとなっている.詳しくはsvgのtransform属性の項を参照.
- Raphael.matrix
- Matrixオブジェクトを生成する.matrix(a,b,c,d,e,f)
- Element.matrix
- 現在図形要素に掛けられている変形をMatixオブジェクトで取得する.
- Matrix.add
- 行列の足し算を行う.add(a,b,c,d,e,f)もしくはadd(matrix)
- Matrix.clone
- 行列の複製を行う.clone()
- Matrix.invert
- 逆行列を取得する.invert()
- Matrix.rotate
- 変換行列に回転を掛け合わせる.rotate(a,x,y)
- Matrix.scale
- 変換行列に拡大・縮小を掛け合わせる.scale(x,y,cx,cy)
- Matrix.translate
- 変換行列に移動を掛け合わせる.translate(x,y)
- Matrix.split
- 変換行列を原始変形(translate,scale,shear:せん断,rotate)に分解する{dx,dy,scalex,scaley,shear,rotate,isSimple}.isSimpleはrotate,scale,translateのみで構成されていた場合にtrueを返す.split()
- Matrix.toTransformString
- 変換行列のtransform文字列表現を取得する.toTransformString()
- Matrix.x
- 指定した座標を行列変換した後のx座標を取得する.x(x,y)
- Matrix.y
- 指定した座標を行列変換した後のy座標を取得する.y(x,y)
Matrixに対する計算は複数重ねることができるが,その際の変形処理の順は後に行った変形関数から順に実行される†.例えばtranslate→scale→rotateの順にメソッドを実行したのであれば,実際の変形は見た目上rotate→scale→translateの順に行われる.
†ここでは図形から見た変形の視点で説明した.transform属性の項でも述べたが,座標軸の変換からも説明できる.この視点で説明するならメソッドを実行した順に座標軸が変換されていることになる.
パスの描画
パスを描画する場合,svgでのd要素で定義されている操作をそのまま利用することが出来る.また,新たにRコマンドが追加されている.このコマンドを用いるとベジェ曲線と異なり,制御点を指定することなしに自動的にカーブを計算し,滑らかに座標間をつなぐことが出来る.
- R,r
- Catmull-Rom curve.指定した座標を滑らかにつなぐ.
但し,R操作を連続して記述する場合は,間のRの指定は省略しないとエラーとなる.
- その他
- →Path要素におけるd操作を参照.
Raphaëlでの塗りつぶしは自動的にくり貫き処理が行われる.
線の描画の調整
pathに限らずstroke-widthの値が奇数だった場合,レンダリング結果の境界部が汚くなるケースがあり,グラフの描画などでくっきりとした表示をしたい場合に問題となる.これは,2つのピクセルにまたがって線が描画されているためであり,これを回避するには描画位置を0.5px(viewBoxの値によって変化する)ずらすとよい.図形の描画位置を直接弄ってもよいが,viewBoxの描画位置をずらす手もある.但し,描画位置を変更することで新たな問題を引き起こす場合があり,根本的な解決策ではない点に注意する.
ieを除外する結果となるが,グラフィックがsvgであることを確認した上で,属性shape-renderingにcrispEdgesを設定する方法もある.
線の装飾
図形要素のstroke-dasharray属性によりstrokeを破線とすることが出来る.なおRaphaël独自記法となっており,指定可能な値は文字「-(ハイフン)」「.(ピリオド)」「_(スペース)」の組み合わせからなる-.-.-..._-_---_.--.--..の10種類のみで,それ以外は無視される.
stroke-linecapと合わせて丸い破線とすることも出来る.
マーカーの指定
パスではarrow-start,arrow-end属性を用いることで簡単にマーカーを設定することが出来る.マーカーの色はstrokeの値を継承する.マーカーが設定される場所は必ず端点であり,パスの頂点にマーカーを設定することは出来ない.
記述法:[スタイル]-[幅(narrow/midium/wide)]-[長さ(short/midium/long)]
- classic
- 矢じり型
- block
- 三角形型
- open
- 鋭角型
- oval
- 楕円型
- diamond
- 菱形
- none
- 無し
パスの操作に関わるメソッド群
pathオブジェクトにはパスの長さや部分パスを取り出すためのメソッドが定義されている.
- Element.getPointAtLength
- 与えた長さにおける座標を返す{x,y}.getPointAtLength(length)
- Element.getSubpath
- 与えた長さに対するサブパス文字列を返す.この文字列を元にpathを描くことが出来る.getSubpath(from,to)
- Element.getTotalLength
- パスの全長を返す.getTotalLength()
パスに関わるユーティリティメソッド群
この他にもRaphaëlではパスを操作する上で便利な機能を多数提供している.パスの交点を算出するなど,単純ではあるが面倒な処理も簡単に実現することが出来る.Raphaelオブジェクトのもつ関数は図形要素を生成しないため,算出したパス情報をSVGPathElementに適用することも可能だ.
- Raphael.getPointAtLength
- パス文字列の与えた長さにおける座標を返す{x,y}.getPointAtLength(path, length)
- Raphael.getSubpath
- パス文字列の与えた長さに対するサブパス文字列を返す.getSubpath(path, from, to)
- Raphael.getTotalLength
- パス文字列に対するパスの全長を返す.getTotalLength()
- Raphael.parsePathString
- パスを線分の配列とする.parsePathString(path)
- Raphael.path2curve
- パスの線分を見た目を変えず(直線を含めて)全てc操作の配列に変換する.パスの記述形式を揃えることでモーフィング処理が容易に行えるようになる.path2curve(path)
- Raphael.pathIntersection
- パスの交わる部分を取得する.交点の座標,交点の位置(0〜1),交差した線分のインデックス,交差した線分の3次ベジェ曲線の頂点値.pathIntersection(path1,path2)
- Raphael.pathToRelative
- パスの表記を相対座標指定とする.こうすることで先頭の座標を変更するだけでパス全体を移動することが可能となる.
- Raphael.mapPath
- パスを行列で変形し,その結果をpath文字列として取得する.mapPath(path, matrix)
- Raphael.transformPath
- パスをtransform文字列で変形し,その結果をpath文字列で取得する.transformPath(path,transformString)
- Raphael.toMatrix
- パスオブジェクトが描画されている座標系への変換Matrixを取得する.同じ座標系に図形を描画したい場合に利用する.toMatrix(path,transformString)
- Raphael.findDotsAtSegment
- 3次ベジェ曲線を構成する4点(8引数)と曲線の分割位置を与え,新たな制御点を得る.得られる制御点はstart,m,n,endの4つ.
例を示す.元の曲線を"M a,b C c,d e,f g,h"とし,分割点をtとすると,得られる曲線の分割は”M a,b C start.x,start.y m.x,m.y t.x,t.y C n.x,n.y end.x,end.y g,h”となる.分割後も,path全体としては全く同じ形を保つ.
パスの交点を取得する
pathIntersectionメソッドを利用すると,二つのパスの交点の座標を取得することが出来る.戻り値は交点情報の配列となっており,パスの交差回数によって長さが変化する.交点情報オブジェクトには交点の座標を表すx,yプロパティの他,交差したパス切片に相当するbez1,bez2プロパティが含まれている.このプロパティにはCコマンドに相当する座標情報が格納されている.
ベジェ曲線の分割
ベジェ曲線は任意の位置で二つのベジェ曲線に分割できることが数学的に確かめられており,findDotsAtSegmentメソッドを用いることでこの計算を自動的に行うことが出来る.得られたオブジェクトには分割位置に対する座標(x,y)の他に分割後のベジェ曲線の制御点に相当するstart,m,n,endの4座標の情報が含まれている.
座標の判定処理
Raphaelでは様々な座標に関わる判定メソッドが提供されている.
- Raphael.is
- データ型の判定を行う.
- Element.getBBox
- 図形要素の描画領域を矩形範囲オブジェクトとして取得する.getBBox(isWithoutTransform)変形を加味するか
- Raphael.bezierBBox
- ベジェ曲線の描画範囲を矩形範囲オブジェクトとして算出する.bezierBBox()
- Raphael.pathBBox
- パスに対する描画範囲を矩形範囲オブジェクトとして取得する.pathBBox()
- Raphael.isBBoxIntersect
- 矩形範囲オブジェクトが共通部分をもつかどうか.isBBoxIntersect(bbox1, bbox2)
- Raphael.isPointInsideBBox
- 矩形範囲オブジェクトが座標を含むかどうか.isPointInsideBBox(bbox, x,y)
- Raphael.isPointInsidePath
- パス文字列が指定した座標を含むかどうか.isPointInsidePath(pathString, x,y)
- Element.isPointInside
- 指定した座標が図形要素に含まれるかどうかを取得する.isPointInside(x,y)
- Paper.getElementByPoint
- 指定した座標の最も上にある図形オブジェクトを取得する.getElementByPoint(x,y)
※現在仕様を含めたバグが存在しており,期待通りの動きとならない.
- Paper.getElementsByPoint
- 指定した座標の図形オブジェクトのSetを取得する.getElementsByPoint(x,y)
ここで矩形範囲オブジェクトはプロパティにx,y,x1,x2,width,heightをもつもので,Element.getBBoxで得られるオブジェクトはこの条件に適合する.
矩形範囲だけでなく,図形の描画範囲による座標の包含を判定することも可能.
座標を指定して,図形要素を串刺しにすることもできる.※getElementByPointメソッドは現在正しく動作しない.マウスカーソルによる図形の選択を意識しているようだが,中々意図した通りの動作とならないため,getElementsByPointメソッドを使い,最後の図形要素を取得するとよい.
アニメーション処理
アニメーション機構を用いることで,cssにおけるtransitionやanimationのような効果を得ることができる.機能が絞られている分習得はしやすいものの,複雑な動きを実現することはできない.とは言え,面倒ではあるが独自のスクリプトを組むことでいくらでも機能を充実することができるので,それほど気にはならない.
アニメーションを実行するにはElement.animateメソッドを呼び出せば良い.現在の設定値からメソッドに指定した値まで徐々に値を変化させることでアニメーションを表現する.イベント処理と組み合わせれば,任意のタイミングでアニメーションを開始できる.また,setTimeout関数と組み合わせれば,繰り返し処理とすることもできる.なお,animateメソッドでアニメーション化出来るプロパティは次の通り.また色のアニメーションは可能だが,グラデーションのアニメーションまでは対応していない.
- アニメーション化可能な属性
- blur,clip-rect,cx,cy,fill,fill-opacity,font-size,height,opacity,path, r,rx,ry,stroke,stroke-opacity,stroke-width,transform,width,x,y
- Raphael.animation
- Animationオブジェクトを生成する.animation({params},ms,easing,callback)
- Animation.delay
- 指定した遅延時間分,実行開始を指定のミリ秒だけ遅らせる.repeatが設定されていた場合は,そのループ毎に遅延が発生する.
- Animateion.repeat
- 指定した回数分繰り返すAnimationオブジェクトを返す.無制限は文字列"Infinity"を指定する.
- Element.animate
- アニメーションを行う.animate({params},ms,easing,callback)もしくはanimation(animation)
- Element.animateWith
- アニメーションを経過時間を同期する.animateWith(targetElem,targetAnim,ms,easing,callback)もしくはanimateWith(targetElem,targetAnim,animation)
Animationオブジェクトはアニメーション動作の定義を行うもので,Element.animateメソッドの引数に利用する.複数の図形要素のアニメーション処理に使いまわすことが出来る.また,拡張機能としてディレイ効果や,繰り返し設定を簡単に実現することができる.Raphaël内部ではこのAnimationとElementの組みでアニメーションが管理されており,アニメーションを制御する場合にAnimationオブジェクトが必要となる.
Element.animateメソッドは重ねがけすることが出来る.そのため異なる属性に別のアニメーションを施すことで表現の幅が広がる.
Element.animateWithメソッドを用いると,現在動作しているアニメーションとこれから実行しようとするアニメーションの経過時間を合わせることが出来る.
パスの変形アニメーション
Raphaëlが提供するアニメーション処理の真骨頂は,異なる形状のpathを滑らかに変形できるところだろう.強力なpathの変形機能を利用して円を矩形に変形することも可能だ.pathが複数のパーツに分かれていてもアニメーション化可能だが,変形処理自体を制御することができないため,イメージ通りに動かすのは非常に困難である.
transform属性に対するアニメーション
またアニメーションをtransform属性に掛けることも可能だ.インターフェースが統一されているため,判りやすい.
イージング関数の種類
アニメーションを行う際のイージング関数は次のものが提供されている.
- linear
- 線形.
- easeIn
- 加速「<」「easeIn」「ease-in」
- easeOut
- 減速「>」「easeOut」「ease-out」
- easeInOut
- 加減速「>」「easeInOut」「ease-in-out」
- backIn
- 後ろに勢いを付けて.「backIn」「back-in」
- backOut
- 終了点で少しはみ出す.「backOut」「back-out」
- elastic
- 振動.
- bounce
- バウンド効果.
- cubic-bezier
- 3次ベジェ曲線による.「cubic-bezier(x1, y1, x2, y2)」
アニメーションの制御
アニメーションの制御を行うメソッドとしては次が提供されている.しかし,現状動作が不安定なようで,当方の環境では思うような制御が行えなかった.なお,内部エラーが発生するとページ全体のアニメーションが停止してしまう.どうやらアニメーションの管理は一箇所で行なっているようだ.
- Element.setTime
- アニメーションの経過時間を設定する.setTime(animation,time)
- Element.stop
- アニメーションを停止する.stop(),stop(animation)
- Element.pause
- アニメーションを一時停止する.pause(),pause(animation)
- Element.resume
- アニメーションを再開する.resume(),resume(animation)
- Element.status
- アニメーションの状態(0から1のアニメーション実行経過の割合)を取得・設定する.内部で動作しているAnimateオブジェクトを取得する目的でも使える.status()…[{animate,status}],status(animate)…status,status(animate,status)…値の設定
補足)animateAlongメソッドについて
文献によっては図形をpathに沿って動かすanimateAlongメソッドが存在しているが,これはRaphaëlのバージョンアップに伴い削除された.これと同等の機能を実現する場合は,パスの全長をgetTotalLengthメソッドで取得し,getPointAtLengthメソッドで道のりに対する座標を算出して図形の描画基準点を決定すると良い.
リンクの設定
Element.attrメソッドを使ってハイパーリンクを定義することが出来る.href属性でリンク先を,target属性でリンクの表示先ウインドウを指定する.
ユーザーイベント
以下にイベント処理に関わるメソッドを示す.複数のイベントハンドラをまとめて設定可能なhoverやdrag等,便利なものが提供されている.イベントハンドラを登録するメソッドには必ずハンドラを解除するメソッドが提供されている.touch系のイベントはAppleが提唱したごく最近定められたもので,利用できる環境が限られる.
イベントハンドラ内部でのthis変数にはイベントを発生させた図形オブジェクトが設定される.簡単な例を示す.
- Element.click
- 図形要素をクリックした際の処理を設定する.
- Element.dblclick
- 図形要素をダブルクリックした際の処理を設定する.
- Element.drag
- 図形要素をドラッグした際の処理を設定する.drag(onmove, onstart, onend, [mcontext], [scontext], [econtext])
- Element.hover
- 図形要素にカーソルが当たった際の処理を設定する.mouseover,mouseoutイベントを一括して登録することが出来る.hover(f_in, f_out, [in_context], [o_context])
- Element.mousedown
- 図形要素においてマウスボタンを押下した際の処理を設定する.
- Element.mousemove
- 図形要素においてカーソルを動かした際の処理を設定する.
- Element.mouseout
- 図形要素においてカーソルが範囲外に出た際の処理を設定する.
- Element.mouseover
- 図形要素においてカーソルが上に乗った際の処理を設定する.
- Element.mouseup
- 図形要素においてマウスボタンを離した際の処理を設定する.
- Element.touchcancel
- 図形要素においてタッチがキャンセルされた際の処理を設定する.
- Element.touchend
- 図形要素においてタッチが終了された際の処理を設定する.
- Element.touchmove
- 図形要素においてタッチの移動がなされた際の処理を設定する.
- Element.touchstart
- 図形要素においてタッチが開始された際の処理を設定する.
- Element.unXXX
- イベント処理の登録を解除する.unclick等.
ドラッグ操作の拡張
ドラッグ操作による処理は,onDragOverメソッドと組み合わせることで応用範囲が更に広がる.
- Element.onDragOver
- ドラッグオーバー時の処理を設定する.関数の引数にはカーソル直下の要素が渡される.引数無指定でイベントハンドラのバインドを解除する.内部ではdrag.over.[id]というカスタムイベントが実行されている.
eveによるカスタムイベントの実行とアニメーションイベント
先に示したものはdomが発生するイベントだが,Raphaëlには同作者によるjavascriptライブラリeveが組み込まれており,カスタムイベントの定義・実行が可能である.
カスタムイベントは,「/」「.」で区切られた文字列で管理されている.例えばeve.onメソッドでイベント「a.b.c」を発生させると,階層順に「a」→「a.b」→「a.b.c」の順番でそれぞれ登録されている関数が実行される.また,ワイルドカード「*」を使って関数を登録することも可能である.ワイルドカードの方が先に実行されるが,詳細はサンプルを参照のこと.
- eve
- カスタムイベントを実行する.eve(name,scope,varargs)scope…実行コンテクスト,varargs…イベントハンドラーに渡されるパラメータ
- eve.listeners
- イベントハンドラ関数の配列を取得する.listeners(name)
- eve.nt
- 現在実行しているイベントの名称を取得する.イベントハンドラ関数内部で実行する.nt(subname)…指定した名称を含んでいるか.nt()現在のイベント名称を取得する.
- eve.on
- イベントハンドラを定義する.on(name,f)
- eve.once
- イベントハンドラを定義する.一度実行されると,自動的に削除される.onece(name,f)
- eve.stop
- これ以降のサブイベントの着火を停止する.stop()
- eve.unbind
- イベントハンドラを削除する.unbind(name,f)
- eve.version
- 組み込まれているeveのバージョン.
アニメーションイベント
また,この機能を用いてアニメーション開始・実行・終了時のイベント処理を行える.
- raphael.anim.start.[id]
- アニメーション開始時に実行される処理の識別子.
- raphael.anim.frame.[id]
- アニメーション中に実行される処理の識別子.
- raphael.anim.finish.[id]
- アニメーション終了時に実行される処理の識別子.
テキストとフォント
テキストを描画するにはPaper.textメソッドを用いる.このメソッドでは文字列の内容が内部に保持されているため,後から自由に中身を書き換えることが出来る.憶えておくべき点は次の通りである.
- テキストの中身を書き換えるには,text属性を編集すれば良い.
- 改行を挿入する場合は改行部に「\n」を記述する.それ以外のエスケープシーケンスは無視される.
- 利用できる属性にはtext-anchor,font,font-family,font-size,font-weight,font-styleがある.
Raphaëlではもうひとつ文字列を描画する方法として,Paper.printメソッドが提供されている.これはCufón形式のデータを読み込み,フォントとして利用するものだ.Cufónはttfやotf形式のフォントを独自形式のjsonデータに変換し,html文書に表示させるライブラリだ.フォントの外形をpathに変換して利用するため,環境に依存しないフォント描画が期待できる.Paper.printメソッドではテキストの内容をpathに変換しているため,後からテキストの内容を書き換えると言った処理を行うことができない.Paper.printメソッドを用いるには次のようにする.
- Raphael.registerFontメソッドを呼び出し,フォントをライブラリに登録する.
- Paper.getFontフォントを呼び出し,fontオブジェクトを呼び出す.その際のフォント名称にはフォントデータjsonのfont-family属性の値を指定する.
- Paper.printメソッドにてテキストを描画する.
- Raphael.registerFont
- フォントを登録する.registerFont(cufon形式のフォントデータjson)
- Paper.getFont
- フォント設定をオブジェクトとして利用する.getFont(フォント登録名,font-weight,font-style,font-stretch)※フォント登録名以下の指定はフォントデータ内に該当するデータが存在しない場合は無効.
- Paper.print
- 文字を描画する.描画の基準は左端からとなる.得られる結果はpath図形オブジェクトとなる.print(x,y,string,font,[size],[origin],[letter_spacing])※originはbaselineかmiddleを指定する.
参考:下の例を実行する前に行なっている処理.(抜粋)
Raphael.registerFont({w:229,face:{"font-family":"whoa","font-weight":400,"font-stretch":"normal","units-per-em":"360","panose-1":"2 0 5 0 0 0 0 0 0 0",ascent:"288",descent:"-72",bbox:"-0.738298 -325 315.086 19","underline-thickness":"26.3672","underline-position":"-24.9609","unicode-range":"U+0020-U+007A"},glyphs:{" ":{w:105},"\u00a0":{w:105},"!":{d:"87,-270v25,40,-13,187,-18,168v-…
なお,日本語環境ではフォント全体を変換するとデータ量が膨大となってしまうので,使いたい文字のみを抽出するようにしたい.
要素ツリーの構造に関わるメソッド群
ベクタグラフィックは内部で(フラットな)階層構造をとるが,要素の順番は図形の重なり順に相当する.先に見た,Element.toFront/toBackはこの順番を入れ替えることで図形の上下関係を変更している.この他図形要素の操作を行うメソッドとしては次のようなものが提供されている.
- Element.clone
- 図形要素を複製する.
- Element.id
- 図形要素の内部識別子を取得するプロパティ.
- Paper.getById
- 図形要素の内部識別子を使って要素を取得する.
- Element.insertAfter
- 図形要素を指定した要素の前に挿入する.つまりその要素の下に描画される.insertAfter(element)
- Element.insertBefore
- 図形要素を指定した要素の後に挿入する.つまりその要素の上に描画される.insertBefore(element)
- Element.prev
- 要素ツリーにおける前の要素への参照.つまりその要素の下の要素を取得する.一番下の要素はnullを返す.
- Element.next
- 要素ツリーにおける次の要素への参照.つまりその要素の上の要素を取得する.一番上の要素はnullを返す.
- Element.remove
- 図形要素を描画対象から外す(Paperオブジェクトの管理下から外す).
- Element.data
- 図形要素に付加的な値を設定する.data(key)…値の取得,data(key,value)…値の設定
- Element.removeData
- 図形要素に付加された値を削除する.removeData(key)
- Element.paper
- 図形要素が所属しているPaperオブジェクト.
- Element.node
- 図形要素に対するdom要素を取得する.node()
このうちElement.nodeメソッドはRaphaëlからよりネイティブなapiにアクセスするために重要な機能で,Raphael.typeでベクタグラフィックの判定処理と組み合わせれば特定の環境での機能を強化する(例えばSVGAnimateElementを追加する)と言った事が可能だ.なお,jQueryに存在した($(HTMLElement)のような)ネイティブapiからRaphaëlオブジェクトにアクセスすると言った機能は存在しない.
ユーティリティーメソッド群
Raphaelでは様々な変換メソッドが提供されている.path文字列やtransform文字列を構成するのに便利だ.
- Raphael.is
- typeof関数のエイリアス.
- Raphael.angle
- 3頂点を指定し,2辺に挟まれた角の角度を取得する.angle(x1,y1,x2,y2,x3,y3)線分点1-点3と点2-点3が挟む角の角度.
- Raphael.deg
- ラジアンを角度に変換する.deg(rad)
- Raphael.rad
- 角度をラジアンに変換する.rad(deg)
- Raphael.format
- 文字列を構築する.fotmat([テンプレート],値0,値1…)
- Raphael.fullfill
- 文字列を構築する.fullfill([テンプレート],連想配列)
- Raphael.snapTo
- 値をグリッドに合わせる.grid値±toleranceの範囲内にvalueが含まれていた場合,grid値が返される.マウスなどで図形要素を移動させる際,描画の基準をグリッドに固定したい場合に用いる.snapTo(grid_values,value,tolerance)
- Raphael.createUUID
- RFC4122形式のUUID文字列を生成する.
環境に関わるメソッド
クロスブラウザを謳うRaphaëlではあるが,環境によっては描画に不具合が出るケースがある.そのような不具合を解消するためのメソッドが提供されている.なお全ての問題が解決するわけではないので,あくまでサポート的な用途として捉える.
- Raphael.svg
- ブラウザがsvgをサポートするかを判定する.
- Raphael.vml
- ブラウザがvmlをサポートするかを判定する.
- Raphael.type
- ブラウザがサポートするベクタグラフィックの種類を取得する.type()SVG/VML
- Raphael.renderfix
- Firefox,ie9におけるサブピクセルのレンダリングに関わる不具合を解消する.
- Raphael.safari
- safari(webkit)におけるレンダリングの不具合を解消する.webkitではアニメーション処理後に稀に描画のゴミが発生する.このゴミを消すために画面を再描画する.
機能の拡張
jQuery等のライブラリと同様にRaphaëlにおいても機能の拡張を行うための仕組みが提供されている.よく使う操作を登録しておくことで,開発効率が上がることだろう.
- Raphael.fn
- Paperオブジェクトに独自メソッドを定義する.
- Raphael.st
- Setオブジェクトに独自メソッドを定義する.
- Raphael.el
- 図形オブジェクトに独自メソッドを定義する.
- Paper.customAttribute
- カスタム属性を定義する.この属性はElement.attr,Elemnet.animateメソッド等から呼び出すことが出来る.
- Paper.ca
- customAttributeへのショートカット.
- Raphael.ninja
- callするとグローバル変数Raphael変数を削除する.万が一の名称の衝突を解消する為の機構.
実行例:(function(raphael){var paper = raphael("canvas", 200,200);//Raphaelを使った処理})(Raphael.ninja());
機能の拡張はプラグインの形をとって様々なものがweb上で配布されている.目的にあったプラグインを導入することで,作業効率を大幅に上げることが出来るだろう.
svg画像をRaphaëlで読み込む方法
Raphaël自体にsvg画像をロードする機能は存在しないが,事前にsvg画像ファイルをRaphaëlで処理可能なjson形式に変換することでブラウザ上に表示させることは可能である.svgは単なるxmlファイルであるから,テキストファイルを扱えるプログラム言語であれば,原理的にはどのようにでも変換可能だが,ここではxslを使った変換を紹介する.xslはxml文書に対する見た目を制御する言語だが,プレーンテキストの出力も可能であり,この機能を用いてsvgからjsファイルへ変換するのが最も簡単だと思われる.なお,xslの使い方についてはスタイル指定の項で触れたので,そちらを参照して欲しい.
下記はsvgファイルからpath要素を取得し,fill,stroke,stroke-width,opacity,fill-opacity,stroke-opacigyの各属性をRaphaëlで読み込み可能なjson形式にするxslのサンプルコードである.機能を必要最低限度に絞った分,コード量はかなり少なめである.
<?xml version="1.0" standalone="no"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:svg="http://www.w3.org/2000/svg">
<xsl:output method="text" encoding="utf-8" omit-xml-declaration="yes" indent="no"/>
<xsl:template match="svg:svg">
//svgからpath要素を抽出してRaphaelで読み込み可能なjson形式に変換する.
var elements = [{type:"path", path:"m 0,0"}
<xsl:apply-templates/>
];
</xsl:template>
<xsl:template match="svg:path">
,{type:"path",path:"<xsl:value-of select="string(@d)"/>",
<!--fill-->
<xsl:if test="not(@fill)">
fill:"black",
</xsl:if>
<xsl:if test="@fill">
fill:"<xsl:value-of select="string(@fill)"/>",
</xsl:if>
<!--stroke-->
<xsl:if test="not(@stroke)">
stroke:"none",
</xsl:if>
<xsl:if test="@stroke">
stroke:"<xsl:value-of select="string(@stroke)"/>",
</xsl:if>
<!--stroke-width-->
<xsl:if test="@stroke-width">
"stroke-width":"<xsl:value-of select="string(@stroke-width)"/>",
</xsl:if>
<xsl:if test="not(@stroke-width)">
"stroke-width":"1",
</xsl:if>
<!--opacity-->
<xsl:if test="@opacity">
opacity:"<xsl:value-of select="string(@opacity)"/>",
</xsl:if>
<xsl:if test="@fill-opacity">
"fill-opacity":"<xsl:value-of select="string(@fill-opacity)"/>",
</xsl:if>
<xsl:if test="@stroke-opacity">
"stroke-opacity":"<xsl:value-of select="string(@stroke-opacity)"/>"
</xsl:if>
id:"<xsl:value-of select="string(@id)"/>"
}
</xsl:template>
</xsl:stylesheet>
これを任意のsvgに適用して目的のjsonコードを得るのだが,その際に注意すべき点がある.これらの処理は事前にドローイングツールのsvg出力設定で指定可能な場合がある.
- path要素だけで画像を作る.処理対象の要素を絞ることでxsl等の処理を簡略化することが出来る.なおrect要素やcircle要素も同様にして処理可能だがここでは省略した.
- style属性を使わない.style属性で見た目を設定していた場合,内容を分解せねばならず,xsl等でRaphaël形式のjsonコードに変換する際の妨げとなる.(この意味でinkscapeとRaphaëlとは食い合わせが悪い.inkscapeではstyle属性での色の指定しかできない.)
- フラットな構造とする.グループ化は最終的に解除する.
- svgとRaphaëlとで仕様が異なるため,fillやstrokeにはグラデーションなどの設定は使わないで,単色での指定をする.
何れも短いコードで実用的な処理結果を得るためのものだが,もちろんもっと複雑な処理を行うことも出来る.しかしそのような処理はsvgの仕様をjavascriptで再定義している事にほかならない.事前にツールによる出力結果に制約を掛ける方が得策である.
なおjsonデータを一旦elementsグローバル変数に格納し,paper.addメソッドに渡しているが,コールバック関数を使った処理としても良い.要件によって自由に書き換えて使って欲しい.実際はかなりの制約が存在するので利用できる場面は限られてしまうが,憶えておいて損はないだろう.
svgdomとの連携
レガシーie環境との互換性を無視することとなるが,Raphaëlをsvg図形描画のユーティリティーとして利用し,svgdomによる描画処理とを混在させることもできる.Raphaël内部で管理されているグラフィック構造を破壊しないようにする必要があるが,Raphaël単体では無理な機能(例えば外部svgファイルで設定されているグラデーションやクリップパス情報の参照等)をsvgdomの機能を使って実現することが可能である.
この他にもRaphaëlが生成したsvgソースを取得すると言ったこともできる.