FlashからCreateJSに出力されたCanvasアニメーションをスマフォ用に組み込んだ断片メモ

CreateJSのコードを組み込んでみた

前提、ともかく現在の仕事においてアニメーションの導入という要件が出てきて、JavaScript書いてる者としてどのように解決しようかと相談した結果、下記の状況・前提からFlashのToolkit for CreateJSを利用することに決めた次第。

  • 自分でアニメーションの動きを作ったことない
  • アニメーターな方がFlasher
  • 取り急ぎカッコ良く動いて欲しい
  • が、今後を考えるとCSSでFlashの動きから書き起こすのダルい

という理由から下記のソリューションを導入する方向になりました。(今の時点で本当にデプロイまで至れるかは、まだ手探りなので参考までに)

これまでCanvasは避けて通って参りましたので、なんというか右も左も分からず。とりあえずFlasherの方にアニメーションを作成していただいて、CreateJSのコードにはき出してもらいました。

大まかな組み込み

はき出してもらうとHTML・JavaScript・リソース画像が生成されて、HTMLをブラウザで表示するとアニメーションを再生できます。

必要なのは主にJavaScriptとリソース画像なので、HTMLに含まれるアニメーションの初期化スクリプトは大雑把にCreateJSのプリロードと適用周りをむりやりModelっぽくしてみたりウーン...(Gist)な感じでModelに仕立てて隠蔽。

manifestsのsrcにDataURIを指定する

PreloadJS#loadManifestに渡すmanifestsに含まれるsrcをおもむろにdataURIに変換したところ、ナチュラルに「XMLHttpRequest cannot load data:image/png;base64,(略). Cross origin requests are only supported for HTTP.」と怒られてしまいドキュメントを漁ったら…

Supported types are defined on PreloadJS, such as PreloadJS.IMAGE. It is recommended that a type is specified when a non-standard file URI (such as a php script) us used.

とのことだったので、type: createjs.PreloadJS.IMAGEとすれば良いということが判明。が、manifestsを別のjsonにしていたので定数ではなくて中身を渡すことに。キチャナイ(´・ω・`)

{
  "manifests": [
    {"src":"img/aaa.png", "id":"aaa", "type":"image"},
    {"src":"img/bbb.png", "id":"bbb", "type":"image"},
    {"src":"img/ccc.png", "id":"ccc", "type":"image"},
  ]
}

はて、"type": "image"なのにBeforeのほうは普通にXHRで取得されてるっぽい謎。dataURIへの変換は、ahomu/grunt-data-uri · GitHubの正規表現をいじってゆるふわ対応させてます。らくちん。

Canvas要素とstageの関連付け

単純な操作として「clickしたらアニメーションが動く」ぐらいで良かったので、canvas要素のclickイベントからstage.doAnimate()的な動作ができるように。でまぁ、canvasに直接stageの参照を生やすのもアレなので、stageのidを使ってインデックス化。

// (あくまで)壮大なイメージ
var indexRelatedPair = {};

// canvasにアニメーションのstageが適用されるときに読んでメモる
function setRelate(canvas, stage) {
  canvas.uid = stage.id;
  indexRelatedPair[canvas.uid] = stage;
}

// 取得できるようにしておく
function getRelatedStage(canvas) {
  return indexRelatedPair[canvas.uid];
}

// clickしたらstageを呼んで実行
canvas.addEventListener('click', function() {
  var stage = getRelatedStage(this.currentTarget);

  // animation定義jsの初期化時に、生やして置いたメソッド
  stage.doAnimate();
}, false);

stage.onClick = function() { … }にしてないのは、現在導入しているftlabs/fastclick · GitHubとバッティングしている為。Canvasのクリックイベントでstageのアニメーションを走らせるだけなので今回はセーフ。

デストラクタ

BackboneのViewとイベントリスナを始末するが如く、ことあるごとに使い終わったstageがもつchildrenからの参照を外します。oldStage.removeAllChildren();な感じで、Viewの切り替え時や同一のCanvasにあたらしいアニメーションが割り当てられたときに、デストラクタ関数として自動で走るように組み込みます。

吐き出しコードのネームスペースの扱いが大雑把

lib, images, createjsが思いっきりグローバルなネームスペースを汚染するようコードが吐き出されています。

今のところライブラリの導入含めて自分でコントロールしている環境ですし、引き継ぐ相手も限られているので、READMEで申し渡しておけば事故る心配も少ないっちゃ少ないのですが、こう(;´〜`)ってなる感じ。

ビルド時に吐き出しコードを加工する手もあるとは思いますが、毎回の生成物をそこまでいじるのも何なので、ひとまずノータッチ。

Android + Canvasマジ鬼門

案の定ですが、一通り組み込んだところでGalaxy S3にて動作確認をしてみたら憤死しました。Android 2.3のXperia Acroで見たら動きが非常に遅いだけで一応動いていたというのに、それすらも上回るバグりっぷり。

_人人人人人人人人人人人人人_
> 電池パック膨らんでしまえ <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y ̄

styleでリサイズするとGalaxy S3開いたら固まる

高解像度向けに、2倍でつくっておいたCanvasまるごとをstyleで1/2にリサイズすると、それだけで標準ブラウザが死ぬことが判明。これは今のところ解決していない。うんこ。とりあえず等倍に振り替えて対応予定。

Galaxy S3で最初表示されるけどすぐに消える

一番最初は問題なく表示されるのですが、すぐにふっと消えてしまう怪奇現象。以下の2つのいずれかで回避できました。が、前者は問題外ですね…。

  • stage.setFPS(5)
  • -webkit-backface-visibility: hidden

前者は、Samsung Galaxy S3 - stock Android 4.04 browser freezes/crashes on stage.update() / EaselJS / Discussion Area - CreateJS Supportで同種の現象に遭遇したひとが、setFPS(5)にしたら消えなくなったと仰っていたので真似しただけ。

後者は、なんというか色々と原理のよく分かっていない描画負荷を軽減してくれそうな魔法の類。Mobile Safariでビットマップ画像をrotateすると荒れるのを回避する - inkdesignでも触れられているので、興味がある方はご参考までに。

立ちはだかるWebViewの壁(予定)

そしてこれ、Cordova的なネイティブアプリでWebView越しにも使用される予定なんですが…嫌な予感しかしませんわ。