TECHNOLOGY 2014.02.05

【前編】気軽かつ高機能なベクターベースの2次元描画ライブラリTwo.jsのご紹介

  • HTML5/CSS3
  • javascript

こんにちは、フロントエンド・エンジニアの荒井です。
今回はHTML5のSVG, Canvas, WebGLに対応したJavaScriptの2次元描画ライブラリ「Two.js」のご紹介です。

Two.jsではベクターベースの2次元描画機能を一通り揃えており、四角形や円(楕円も含む)などの基本的な図形を描画したり、直線/曲線を繋げて独自の図形を描画する事が可能になります。
Flash-ActionScriptで言う所のGraphicsクラスに相当する機能になるかと思います。

これだけであれば想像通りの機能だとは思いますが、Two.jsが面白いのはその図形を構成している直線/曲線の頂点(コントロールポイントも含む)の座標を後から操作出来るようになっているという点です。
これによって、図形のひとつひとつの頂点を動かす事で、ビットマップベースとは一味違ったアニメーションを作成する事が出来るようになります。

さらにもうひとつ私が面白いと思ったのはSVGデータを取り込んで使用出来る機能です。
例えばIllustratorで作成したベクターデータをSVG形式で保存してHTMLに埋め込むだけで、アニメーションの素材として使用できるようになります。

機能面だけでもかなり面白いな~という印象ですが、実際にTwo.jsを触ってみて思ったのはとにかく気軽に図形を描画出来るという事でした。
Two.jsでは内部にUnderscore.jsBackbone.jsを組み込んでいるので、eachを始めとしたコレクション操作やイベントバインディングなどのユーティリティメソッドがそのまま使えるのが便利でとても良いですね。

今回はそんなTwo.jsを使って、SVGデータを元にした図形のアニメーションを作成する基本的な流れを説明したいと思います。
ちょっと長くなりそうなので前編と後編に分けてお送りする事にして、前編ではTwo.jsの基本的な使い方とSVGデータを取り込んで図形を描画する所までを説明します。
図形をアニメーションさせる所は次回の後編でお話ししようと思います。

SVGデータを元にしたアニメーションのサンプル

百聞は一見に如かずと思いますので、まずはサンプルをお見せします。

頂点数の少ないものと多いものの2種類の素材を用意し、さらに各々をSVG, Canvas, WebGLのレンダリング方法が異なるパターンに分けてみました。

IN VOGUE ロゴ アニメーション (低スペック向け)

チェ・ゲバラ 肖像画 アニメーション (高スペック向け)

マウスカーソルをロゴに近付けるとその付近の線がブルっと震えます。
よくある手法ですが、 図形を構成する頂点に対してマウスカーソルが近づくと避け、遠ざかると元の位置に戻る ようにしてアニメーションさせています。

Two.js を使ってSVGデータを元に図形を配置

ここからは上記サンプルのようにSVGデータを取り込んで配置する所までを、Two.jsの基本的な使い方と合わせて説明していきます。
なお、基本的な図形の生成の仕方は公式サイトのBasic Usageを参考にされると良いと思います。
円や四角形を生成してアニメーションさせるコードと動作サンプルがあります。

Twoインスタンスの生成

初めに「Two」インスタンスを生成します。その際にオプションを連想配列のオブジェクトで指定します。

var two = new Two({ type: Two.Types.svg, width: 1280, height:720 });

オプションの「type」でレンダリング方法を指定する事が出来ます。
渡せるキーは「Two.Types.svg」、「Two.Types.canvas」、「Two.Types.webgl」の3種類で、デフォルトは「Two.Types.svg」になっています。

ちなみに、ブラウザによって自動的にレンダリング方法を変える仕組みにはなっていないようなので、(まだサポートされているブラウザが少ない)WebGLを使いたい場合はブラウザに応じてレンダリング方法を変えるコードを自分で書く必要がありますね。
WebGL未対応のブラウザでは自動でCanvasにフォールバックしてくれる「CreateJS」とは勝手が違うので注意が必要かと思います。

DOMへの配置

続いて、生成したTwoインスタンスの「appendTo」メソッドでDOM要素を指定して配置先を決めます。 ここでは、contentというidを持つ<div>要素の中に配置しています。

// HTML上に<div id="content"></div>が配置されているものとします。

// DOMの配置先を指定
two.appendTo(document.getElementById('content'));

なお、レンダリング方法がSVGの場合は<svg>要素が、CanvasあるいはWebGLの場合は<canvas>要素が生成され、 appendTo で指定したDOM要素の中に配置されるようです。

ツリー構造の表示リスト

一旦ここで、Two.jsが持つ表示リストのツリー構造について軽く説明しておきます。

Two → ( Two.Gruop → Two.Gruop ... ) → Two.Polygon

Two.Polygon

Two.jsでは「Two.Polygon」というクラスが図形を表していて、構成する頂点データが含まれています。
また、図形単体の線や塗り、位置・拡大縮小・回転角度といった情報も持っています。
これが無いと図形が表示されませんので、なにはともあれ必要な要素となります。

Two.Gruop

「Two.Gruop」は、いわゆる表示オブジェクトコンテナーの役割を持つクラスで、複数の「Two.Polygon」インスタンスをまとめて相対的に位置・拡大縮小・回転角度を操作する事ができます。
さらに、含まれる全ての図形の線や塗りの情報を一括で指定するための便利なメソッドも用意されています。
「Two.Gruop」はもちろん入れ子にする事も出来ます。

Two

「Two」はTwo.jsの全体の機能を管理するクラスでもありますが、表示リストのルートコンテナーの役割も持っています。
「Two」インスタンスから辿って表示リストに含まれていない図形は画面に表示されません。

なお、「Two.makeCircle」メソッド(正円のTwo.Polygonを生成してくれる)のように「Two」インスタンスを経由して生成した図形は自動的に「Two」インスタンスの直下に配置されます。
このようにすぐに描画に直結するようなメソッドが用意されているのも、Two.jsの気軽さのひとつではないかと思います。

SVGデータから図形の生成

ここからは話を戻して、SVGデータから図形を生成する方法について説明します。

// <svg id="logo" ... >...</svg>でHTML上にSVGデータが配置されているものとします。
// SVGからTwo.Gruopを生成
var shape = two.interpret(document.getElementById('logo'));

// 線と塗り
shape.stroke = 'black';
shape.linewidth = 5;
shape.fill = 'gray';

「two.interpret」に<svg>要素を渡すだけです。
とてもシンプルで良いですね!

これだけでSVGデータを元に「Two.Polygon」を集合させた「Two.Gruop」インスタンスが生成されて「shape」変数に格納されます。
また、前述のとおり「two.interpret」メソッドもデフォルトで「Two」インスタンスの直下に配置されるのでこの時点で描画の対象になります。

その後の行では図形全体の塗りと線の情報を指定していて、今回は塗りはグレイ、線は5pxのブラックとしています。

描画の開始

two.play();

描画を開始するには「Two.play」メソッドを呼び出します。
このメソッドを呼び出した後は、自動的に requestAnimationFrame 使って定期的に描画を更新してくれるようになります。

Two.jsには手動で描画を更新する「Two.update」メソッドも用意されていますが、「Two」インスタンスは 描画を更新する直前に「update」イベントを発行してくれるので、このイベントの中でアニメーションさせる処理を書く様にしてしまえば、手動で更新する必要は特にありません。
この辺りは後編で説明したいと思います。

ちなみに、最初の「Two」インスタンスを生成する時にオプションで「autostart」をtrueにしておくと、「Two.play」メソッドが実行されたものとして、自動で描画が開始されるようになります。

var two = new Two({ type: Two.Types.svg, width: 1280, height:720, autostart: true });

これで、とりあえずSVGデータを元に図形を画面に表示させる所までは出来る様になりました。

出来上がったサンプルがこちらになります。
http://lab.invogue.jp/y-arai/01_twojs/sample/
JavaScriptのコードを見て頂くと分かると思いますが、数行のコードでここまでを実現しています。

次回は、この図形を元にアニメーションさせる方法について説明したいと思います。
乞うご期待を!