CreateJSで遊んでみる 1日目 EaselJS

2015.06.16 記事の内容を全面的に書き直しました。

目次

CreateJS とは何者なのか?

CreateJSはHTML5の技術を利用した インタラクティブなコンテンツを作成するためのJavaScriptのライブラリ群です。

@gskinner 氏が開発をしているとのこともあり、 Flashで開発されていた方であれば ActionScript で利用できたクラスやメソッドと同等のものが多く用意されており、導入コストを下げることができるのではないでしょうか。

Adobe Flash Pro CS6 からは Toolkit for CreateJS という機能を使って、 Flashで作成したアニメーションを CreateJS を利用したソースコードに変換して書き出すといったこともできます。

Adobe Flash Pro が無いと CreateJS を利用することができないと思われている方もいるかもしれませんが、CraateJS は ライブラリ単体でも利用することが可能です。

EaselJS をはじめるための準備

今回は EaselJSを触っていこうと思います。

EaselJS は Canvas の API を扱いやすくしてくれる CreateJS を構成するライブラリの一つです。

http://www.createjs.com/downloads

上の URL 先にある CDN、DOWNLOAD から選んで EaselJS を CDN で提供されている URL または ファイル一式をダウンロードすることが出来ます。

また、公式の CreateJS | Getting Started では、Hello World から多くのチュートリアルが掲載されていますので、目を通しておくとよいかと思います。

EaselJS を使うメリット

EaselJS には Canvas でインタラクティブなコンテンツを作成するための機能が沢山提供されているのですが、EaselJS を使わない場合と比べるとどのようなメリットがあるのでしょうか。

Canvas にはオブジェクトを管理する機能はない

Canvas には円やベジエ曲線などを描くための API が用意されていますが、描画したものをオブジェクトとして管理するための機能がありません。

そのため、描画したものをオブジェクトとして扱うためには、オブジェクトに位置や描画の情報を保持するための箱を独自で用意しなければいけません。

EaselJS では位置情報や重なりの情報など表示を行うための情報を各オブジェクトが持っているので、先述のような手間を省き、コンテンツの演出に時間を割くことができるようになります。

Canvas はウォールペイントのようなもの?

Canvas はウォールペイントのようなもの?

Canvas で出力されているのはビットマップ画像です。 例えると、壁のなどにスプレーで描かれているようなウォールペイントのようなものでしょうか。

ウォールペイントは一度描いてしまうと自動的に消えることはありません。(天候や風化によるものは除く)

絵にヒゲを追加したとしても最初に書いた絵は消えません。 絵の中の男性を動かしたいと思ったらどうしたらいいでしょうか?


アニメに出てくるような呪文を唱えたところで絵の中の男性は動き出すことはないので、絵の上からペンキを塗って、その上に移動した後の男性の絵を描きます。

もしくは壁ごと取り替えて描き直すことでも絵を動かすことができますね!

もちろん男性の外側にも絵が描かれていた場合にはその絵も再現しましょう。そこからまた動かしたいとなった時には先述の工程を繰り返していきます。

これは Canvas も同じです。アニメーションを行う際には1フレームごとに絵の描いてある場所を一度消して、その上から塗り直す処理を書く必要があります。


EaselJS では Stage クラスという表示リストのようなものが用意されているので、各オブジェクトはこのインスタンスのリストに追加していくことで Canvas 上の個々のオブジェクトを管理できる仕組みになっています。

オブジェクトがStageに追加されるイメージ図

Stage.update() というメソッドを実行することで、Canvas を一度クリアして Stage上のオブジェクトを Canvas へと反映させることができます。


参考・引用 ) EaselJS の Stage クラス の update メソッドの中身

以下のコードは先述の説明で触れた Stage.update() が定義されている行です。1フレーム更新するだけでこれだけの処理が走っていますが、制作者は update というメソッドを知っているだけで裏側で何が起きているのかを気にすることなく制作を行っていくことができます。

http://www.createjs.com/docs/easeljs/files/easeljs_display_Stage.js.html#l363 より

p.update = function(props) {
    if (!this.canvas) { return; }
    if (this.tickOnUpdate) { this.tick(props); }
    if (this.dispatchEvent("drawstart", false, true) === false) { return; }
    createjs.DisplayObject._snapToPixelEnabled = this.snapToPixelEnabled;
    var r = this.drawRect, ctx = this.canvas.getContext("2d");
    ctx.setTransform(1, 0, 0, 1, 0, 0);
    if (this.autoClear) {
        if (r) { ctx.clearRect(r.x, r.y, r.width, r.height); }
        else { ctx.clearRect(0, 0, this.canvas.width+1, this.canvas.height+1); }
    }
    ctx.save();
    if (this.drawRect) {
        ctx.beginPath();
        ctx.rect(r.x, r.y, r.width, r.height);
        ctx.clip();
    }
    this.updateContext(ctx);
    this.draw(ctx, false);
    ctx.restore();
    this.dispatchEvent("drawend");
};

これによりオブジェクトを移動させるアニメーションを作る際に Canvas をクリアして、描画し直すための命令文を書かずに済むようになります。

ベクター図形の描画がよりわかりやすく便利に

Canvas 上で図形を描画する場合、用意されている API を利用して描画をしていきますが、塗りと描画が同時に行えなかったり、円を描くだけでも arc という弧を描くメソッドを利用して書く必要があったりとコードが煩雑になることあります。

EaselJS では描画コマンドをメソッドチェーンで描くことができたり、Canvas で使える描画 API に加えて、円を描くための drawCircle や星を描くための drawPolyStar ,Illustrator などで作成したパスを BASE64 の形式で吐き出すことで EaselJS で読み込むことができる decodePath といったような便利なコマンドが追加されています。

実際にコードを比較してみましょう。

以下のコードは Canvas 上に円を一つ描画するためのコードを Canvas の API を利用したものと EaselJS を利用したもので比較したものです。

CanvasAPI を使った場合

var canvas = document.getElementById("canvas")
var ctx = canvas.getContext("2d")

ctx.beginPath()
ctx.fillStyle = 'black'
ctx.arc(40, 40, 40, 0, 360, false)
ctx.closePath()
ctx.fill()

EaselJS を使った場合

var canvas = document.getElementById("canvas")
var stage = new createjs.Stage(canvas)

circle = new createjs.Shape();
circle.graphics.beginFill('black').drawCircle(40, 40, 40);

stage.addChild(circle)
stage.update()

豊富なフィルター機能

EaselJS にはフィルター系のクラスがたくさん用意されています。

AlphaMapFilter, AlphaMaskFilter, ColorMatrixFilter, BlurFilter などがそれらに該当します。

それぞれがどのような機能なのかを確認するのには、公式サイトのデモを確認するのが一番早いです。

http://www.createjs.com/demos/easeljs/filters http://www.createjs.com/demos/easeljs/alphamaskreveal

フィルター機能を一から実装するのはなかなか骨の折れる作業になりそうなのでとても助かりますね。

これらの機能は EaselJS の魅力な機能のほんの一部でしかありませんが、Canvas を扱う上ではなかなか便利そうライブラリだということが感じてもらえたのではないでしょうか。

次項からは実際に EaselJS を触りながらどのようなことができるのかを見ていきます。

実際に触ってみる

CreateJS を使うための準備

EaselJS を利用するための準備をしていきます。 CDN または ダウンロードしてきた EaselJS を Script 要素を使って読み込みます。

<script src="https://code.createjs.com/easeljs-0.8.1.min.js"></script>

描画を行うための Canvas 要素を HTML上 に配置します。

<canvas id="canvas" width="300" height="300"></canvas>

EaselJS の処理を書くために別ファイルで JavaScript ファイルを用意しておき、ここに処理を書いていきます。

<script src="app.js"></script>

Stage を配置する

EaselJS を使うための準備が整いました。早速 オブジェクトを配置するための Stage を作ってみます。

EaselJS は、機能をクラスという単位で分けられています。 それぞれのインスタンスを作成し、プロパティやメソッドを利用していきます。

var canvas = document.getElementById("canvas");
var stage = new createjs.Stage(canvas);

ここまでの準備を行うと以下のデモのようになります。

var canvas = document.getElementById("canvas");
var stage = new createjs.Stage(canvas);

See the Pen setup EaselJS by turusuke (@turusuke) on CodePen.

今の時点では CSS で Canvas に背景色をつけてはいますが, Stage を作っただけなので何も表示されていません。 次はここにオブジェクトを置いていってみます。

EaselJS DisplayObjects

See the Pen EaselJS DisplayObjects by turusuke (@turusuke) on CodePen.

上記のデモでは Textクラス,Shapeクラス,Bitmapクラスを利用しています。

コードの量は一気に増えましたが、一つ一つを見ていくと行っていることは至ってシンプルです。

Instance = bitmap = new createjs.xxx()

用意されているクラスから new でインスタンスを作り、用意しておいた Stage クラスのインスタンスへ addchild() を使って追加しています。

stage.addChild(text, circle);
stage.update();

update() を呼ぶことで、Stageの上に追加されたオブジェクトを Canvas に反映させることができます。

このように Stage へ追加できるような オブジェクトは DisplayObject クラスを継承しているため、位置を指定するための x や y、拡大率を変更する scaleX,scaleY、など共通の機能を持っています。

DisplayObject Class

Shepeクラスと Graphicsクラスでベクターシェイプを描く

EaselJS v0.8.1 API Documentation : Shape

EaselJS v0.8.1 API Documentation : Graphics

Shape クラスGraphics クラスを使うことで長方形、円、星形、ベジエ曲線などを使って図形を描画することができます。

Graphics クラスのインスタンスを Shape クラスのインスタンス生成時にオプションとして渡すか、Shape の graphics プロパティを利用する方法があります。

var graphics = new createjs.Graphics().beginFill("red").drawCircle(40, 40, 40);
var shape = new createjs.Shape(graphics);

var shape = new createjs.Shape();
shape.graphics.beginFill("red").drawCircle(40, 40, 40);

Bitmapクラス で画像を読み込む

画像を利用するためには Bitmap クラスを使います。インスタンスを生成する際に画像のURLをオブションとして渡します。

bitmap = new createjs.Bitmap("img.png");

画像の読み込みより先に Canvas への描画の処理が走ってしまうと画像が描画されないので、事前にブラウザに画像を先読みさせておきます。

var img = new Image();
img.src = "https://s3-us-west-2.amazonaws.com/s.cdpn.io/944/chara.png";
img.onload = function() {
  bitmap = new createjs.Bitmap("https://s3-us-west-2.amazonaws.com/s.cdpn.io/944/chara.png");
  stage.addChild(bitmap);
  stage.update();
};

この例では画像の先読みに onload を利用していますが、CreateJS には PreloadJS というファイルを先読みするためのライブラリが用意されていますので、そちらを利用してもよいかとお思います。

Tickerクラスを使ってアニメーションを実装する

表示ができたら次はアニメーションをさせてみましょう。 ここでは Ticker というクラスを利用して、アニメーション表現を実現させていきます。

Ticker は指定された間隔で tick イベントを発生させてくれるクラスで、フレームレートの設定や経過時間の取得も行うことができます。


この tick イベントを addEventListener または on で購読して指定された間隔で処理を行わせます。

Tickerクラスは static interface として提供されているので、インスタンスを作らなくてもプロパティの設定やメソッドを実行させることができます。

var handleTick = function(event){
};

createjs.Ticker.addEventListener("tick", handleTick);

1秒辺りの実行回数(FPS)はデフォルトでは 20FPSになっています。 これを変更するには framerate プロパティに値を設定します。

framerate で指定した値はあくまで目標値で、実行環境や処理内容によっては framerate を落とすことがあるということを覚えておくとよいかと思います。

createjs.Ticker.framerate = 40

framerate を大きくすれば大きくするほど、1秒間の実行回数は増えアニメーションの動きはなめからになりますが、計算量も増えるので注意が必要です。

先ほどまでのデモに以下の処理を加えます。

createjs.Ticker.framerate = 40
createjs.Ticker.addEventListener("tick",function(){
  if(bitmap.x < canvas.width){
    bitmap.x++;
  }else{
    bitmap.x = -100;
  }
  stage.update();
  return;
});

See the Pen EaselJS Ticker Class by turusuke (@turusuke) on CodePen.

フレームごとに Bitmap で Stage上に置いていた画像の x プロパティを +1 して動かして、その後に stage.update() で移動後の状態を Canvas へと反映させています。

ステージより x 座標が大きくなったらステージの x:-100 の位置へと移動させることで繰り返し表示されるようになっています。

MouseEvent でクリックでアクションを起こす

最後にマウスイベントを設定してみます。 以下のデモでは円をクリックすると x方向・y方向の拡大率が 0.1ずつ大きくなるようにしています。

See the Pen EaselJS Click Event by turusuke (@turusuke) on CodePen.

circle に対して addEventListener で click を購読して、第二引数で処理の関数を渡しています。

circle.addEventListener('click',function(){
  circle.scaleX += 0.1;
  circle.scaleY += 0.1;
});

終わりに

この他にもマスクやタッチデバイス対応、WebGL,Flash との連携など紹介しきれていない機能がたくさんあります。

ここまで EaselJS はとても便利だと紹介してきましたが、もちろん弱点はあります。

例えば一度にたくさんのオブジェクトを動かすといった場合などです。沢山の便利なメソッドやプロパティを持って生まれてきたインスタンスを Stage 上で監視しているので、それだけ計算処理も増えます。

そういった場合は EaselJS の機能の利用を最小限して、DisplayObject を使って書いているような部分は Canvas API を利用して書き直すといったリファクタリングを行うことでパフォーマンスが改善されることがあります。

これで1日目は終わりです。 2日目はアニメーションを設定するのには欠かせない TweenJS を紹介したいと思います。