CreateJSでハマったこと

 長らく仕様策定に手間取っていたHTML5が大きく推進することになった理由の一つには、やはりAppleiPhoneFlashへの非対応を発表したことがあるでしょう。HTML5自体はいまだ完全には標準化されていませんが、主要ブラウザでは既にcanvasによる描画処理をサポートするようになっており[1]、今後徐々にFlashからHTML5への移行が進んでゆくものと思われます[2]。

 FlashのプロジェクトをHTMLに変換するツールにはSwiffy(Google)、SWF Animation Converter(マルチでバイスlab.)など何種類かありますが、今のところブラウザ互換性や、ActionScript部分への対応状況などの問題により、これといった決め手がないのが現状です。
 その中で、Adobe Flash Professional CS6にcanvasアニメーション用フレームワークCreateJSへのエクスポート機能を提供するToolkit for CreateJSは、Adobe純正のツール[3]であること、出力されたJSファイルの可読性がよいことから、現状では有力な選択肢の一つであると言えるでしょう。

 さて本題。
 CreateJSは、それ自身はiPhone/Android問わず動作する、極めて互換性の高いフレームワークとなっています(周辺のHTMLやCSSあたりのブラウザ依存性はともかく)。
 ところが先日の開発途中、私はGalaxy SIIIおよびXperia GXでのみ、アニメーション途中に表示崩れが発生したり、ブラウザが突然落ちるという現象に見舞われたのです。
 Galaxy SIIIやXperia GXといえば、OSはAndroid4。Android2系の古いブラウザならともかく、これを理由にAndroid4の機種をサポート対象外とするのは、まあ常識的に考えてありえないわけで。

 かれこれ一週間ほど経った頃でしょうか?
 フレームごとの処理を詳しく見ていったところ、前回のフレーム内容を消去する処理をコメントアウトしたところ、落ちずに動き続けることが判明したのです。
 該当のコードは、次のようになっていました(EaselJS v0.5.0より引用)。

/**
 * Clears the target canvas. Useful if autoClear is set to false.
 * @method clear
 **/
p.clear = function() {
	if (!this.canvas) { return; }
	var ctx = this.canvas.getContext("2d");
	ctx.setTransform(1, 0, 0, 1, 0, 0);
	ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
}

 これは極めて単純なcanvasの消去処理で、どこかに問題があるようには思えません。
 実際、私の実験によると、canvasのサイズが小さい時には問題なく動き、ある程度大きい場合のみ問題が起こることがわかっています。
 そこで、上記のコードを次のように変更してみます。

p.clear = function() {
	if (!this.canvas) { return; }
	var ctx = this.canvas.getContext("2d");
	ctx.setTransform(1, 0, 0, 1, 0, 0);
	var m = (this.canvas.width+255) >> 8;
	var n = (this.canvas.height+255) >> 8;
	for (var i = 0; i < m; ++i) {
		for (var j = 0; j < n; ++j) {
			ctx.clearRect(i << 8, j << 8, 256, 256);
		}
	}
}

 バッチリ。どうも、ある程度以上の大きさの領域をclearRect()すると、上記の機種では何らかのメモリリークが起こっていたようです。


※1 もっとも、HTML上でアニメーションを表現する手段はcanvasに限らず、SVGによるベクターグラフィックや、さらに言えばHTML4でも要素を上手いこと操作することによりアニメーションを作ることはできます。
※2 その際Flashは、swfの作成ツールからアニメーションHTMLの作成ツールとなることでしょう。
※3 以前はAdobe純正のHTML出力ツールとしてWallabyがありましたが、現在は公開停止されているようで、WallabyへのリンクはToolkit for CreateJSへとリダイレクトされています。