かつてWebにはFlashというプラットフォームがありました。Flashはグラフィックの描画に長けており、標準ライブラリだけでも、かなり柔軟な表示を行うことができました。しかしJavaScriptの標準ライブラリにはそういったものがありません。グラフィックを描画するためには、原始的なCanvasAPIを利用するか、外部のライブラリを利用するか、自分でライブラリを作ることになります。

そこで今回は「自分でライブラリを作る」をやってみましょう。Flashライクな表示システムを作ってみるのです。Flash風のAPIを備えたライブラリはすでに存在しますが、自分で作ってみると楽しいですし、いろんな発見があるかもしれません。実用には程遠いかもしれませんが、一度挑戦してみましょう。

Flashの表示の仕組み

まず初めにFlashの「表示リスト」について軽くおさらいしておきましょう。表示リストは次のようなシステムでした。

まず、システムには唯一のStageが存在します。Stageは表示オブジェクトDisplayObjectまたはDisplayObjectContainer)を複数個持つことができ、Stageに追加された表示オブジェクトは画面に表示されます。

表示オブジェクトは、その名の通り画面に表示するためのオブジェクトです。表示オブジェクトは様々なプロパティを持ちます。例えば表示オブジェクトの持つxプロパティやyプロパティを操作すると、画面上での表示もその通りに移動します。

そして、DisplayObjectContainerはその名の通り、自身の子としてさらに表示オブジェクトを持つことができます。

(※実際のFlashのシステムではStageとDisplayObjectの間に、タイムラインを表す表示オブジェクトが存在しますが、今回はタイムラインを作らないため省略しています。)

Flashではこのようにして表示オブジェクトを入れ子にし、階層構造を使って描画を管理していました。これが表示リストです。

JavaScriptでFlashのような表示システムを作る

RenderContext

さて、これからFlash風の表示システムを構築していきます。このライブラリを「Flesh」と名付けましょう!FleshはFlash風のAPIを備えており、単純なグラフィックを簡単に扱うことのできるライブラリです。flesh.jsというファイルを作り、書き込んでいきましょう。

まずcanvas要素の操作をラップするRenderContextクラスを作りましょう。

もちろんRenderCoxtextクラスなど作らずに、直接canvasを操作しても構いません。しかし例えば、描画をWebGLに切り替えたくなった場合、操作をラップしたクラスがないと、煩雑なコードの書き換えが発生することになります。そこで、中間層を提供することで、描画方法の差異を吸収することができます。

もちろん、そういった心配がない場合、例えば絶対にWebGLは使うことがないと分かっている場合、この作業は不要になります。不要な方は、この手順を飛ばしてもらっても構いません。

実際にRenderCotextクラスを定義してみましょう。最低限必要なものは以下の通りです:

このクラスを継承して、canvasのコンテキストを作成しましょう。内容としてはcanvasの同名のメソッドを呼び出すだけです。

これでRenderContextの準備ができました。

DisplayObjectとDisplayObjectContainer

次に一番重要なStageを……と行きたいところですが、まずは表示オブジェクトからです。なぜならStageも表示オブジェクトのひとつなので、先に表示オブジェクトを作らないとStageも作ることができません!

よって最も基本的な表示オブジェクトとなるDisplayObjectを作成しましょう!だいたい以下のようなプロパティを持ちます:

とりあえず、xy座標、回転角度、アルファ、表示・非表示、親要素をプロパティとして持たせました。より多くのプロパティをもたせても構いませんが、ここではシンプルにするため、数を絞りました。

次にDisplayObjectに、必要なgetter/setter、メソッドを実装していきましょう。

DisplayObjectクラスは、すべての根幹となるクラスですが、このクラスのオブジェクトを直接使用するわけではありません。よって実装は最低限でいいでしょう。

renderメソッドは最も重要なメソッドです。これは画面上に表示オブジェクトの内容を描画するメソッドです。しかし先述の通りDisplayObjectクラスを直接使用するわけではないので、ここでは空白にしておいて、それぞれの継承先で実装を行います。

次にDisplayObjectContainerを作りましょう。DisplayObjectContainerはDisplayObjectを継承します。DisplayObjectとの違いは子要素を持つことです。子要素を保持するプロパティと子要素の追加/削除をするメソッドを持ちます。これを実装してみましょう。DisplayObjectの下に追加します。

ここで大切なのはrenderメソッドをオーバーライドしておくことです。DisplayObjectContainerは子要素の描画も行う必要があるので、子要素それぞれについてrenderメソッドを呼び出しておきます。

このとき、DisplayObjectContainerは階層構造を持つことに注意してください。つまり、親が移動した分だけ、子も一緒に移動するということです。これは親要素の平行移動・回転を戻さずに子要素を描画し、子要素の描画が終わってから平行移動・回転を戻すという作業で実現することができます。

GraphicとGraphicCommand

次に、DisplayObjectContainerを継承し、実際にグラフィックの描画が可能な、Spriteクラスを作っていきます。

描画周りの命令はSpriteクラスに直接実装していっても良いのですが、Spriteクラスをシンプルに保つため、Graphicクラスという別のクラスを作り、SpriteオブジェクトにGraphicオブジェクトを持たせる方法を選びます。この方がSpriteのコードがすっきりします。

GraphicクラスはSpriteからの描画命令に応じてGraphicCommandを発行し、描画命令リストを作ります。そして描画の合図(renderメソッド)があったら、すべてのGraphicCommandを実行し、実際に描画します。

この方式の利点は、表示オブジェクトの管理が簡単になることです。描画命令を受け取って即座に描画してしまうと、オブジェクトの親子関係や上下関係の管理が面倒になります。しかし描画命令をためこんでおき、合図があったときに一括して処理することで、依存関係を単純に処理できるようになります。

まずはGraphicCommandを作ってみましょう。GraphicCommandクラスはexecuteメソッドを持つだけの単純なクラスです。

そして各命令に応じたGraphicCommandを作成していきます。内容としては、単純にパラメータを保持して、executeが呼び出されたらcontextに対して命令を行うだけです。

今回はtranslate、rotate、setAlphaの3つは内部的に使用するだけなので、GraphicCommandからは省きました。

次にこれを利用したGraphicクラスを作成しましょう。GraphicクラスはSpriteクラスからの命令に従ってGraphicCommandを発行していくクラスです。基本的には命令どおりのコマンドを発行して配列に追加していくだけで出来上がります。

Graphicクラスにもrenderメソッドはありますが、これは今までと違い、stateという引数を受け取ります。stateはSpriteクラスのことだと思ってもらって構いません。stateには平行移動やアルファ値などの情報が入っています。

描画の際は、平行移動・回転を忘れないように注意してください。描画コマンドの実行が終わった後に元に戻すのも忘れないように。

Sprite

Graphicクラスができたら具体的な表示オブジェクトを作っていきます。この具体的な表示オブジェクトのことをSpriteと名付けます。SpriteクラスはGraphicオブジェクトを持つだけの簡単なクラスです。Spriteは子要素を持つことができるので、DisplayObjectContainerクラスを継承しています。

renderメソッド内において、描画はGraphicオブジェクトに任せます。スーパークラスのメソッド呼び出しを忘れないようにしてください。

Stage

最後に(ようやく!)Stageクラスを作りましょう。Stageは複数の子要素を持つので、当然DisplayObjectContainerになります。

これで完成です!次は実際に動かしてみましょう。

ここまでのflesh.jsコードまとめ

動作確認

静止画像

まずは静止画像で確認してみましょう。main.jsというファイルを作り、flesh.jsの後に読み込みます。

main.jsの中で試しにSpriteを作ってStageに追加してみます。

そしてこれを実行します。

やったー!表示されました!

動かしてみる

次にアニメーションを作ってみましょう。main.jsを編集します。

アニメーションにも成功しました!