iPhoneのブラウザでのアニメーション手法を比較
基本
- 拡大・縮小を避ける
- 下記の指定
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0;">
-
-
- iPhone4の解像度はiPhone3Gより縦・横に倍大きいが、width=device-widthを指定してもiPhone3Gと同じ320pxが返り、表示時に2倍に拡大されて描画される。style指定で見かけ上の拡大を避けれなくはないが、今回はしない。
- img.width / img.heighが画像の実サイズと異なる値を入れたり、canvasでの拡大をすると遅くなるのでしない
-
- タイマーはまとめる
- ゲームと同じように1つのループで行う。タイマーを増やすと遅くなるし、canvasならclearRect()が増える。
- 移動量は経過時間ベースで計算する
- 遅くなってスムーズさが落ちても、全体の完了時間は変わらないようにする。
- 少数の位置で描画しない
- 遅くなるため、CSSのtop, leftやdrawImage()の引数に小数を利用しない。整数化はMath.floor()より~~の方が速い。
比較
iPhone4(MC605J) OS 4.3で比較。
canvas
- キャラクターの数が少ない(5個)場合: 25FPS。スムーズ。"DOMからinline styleのleft/topを変更"よりFPSは低い。
- コンテンツ: http://ndruger.lolipop.jp/hatena/20110617/drawing_performance/drawing_performance.htm?type=canvas&number=5
- FPSの低さは、キャラクターが少ない時にclearRect()を全体に行う場合、clearRect()が占める時間の割合が大きいためと推測される。
- キャラクターの数が多い(80個)場合: 18FPS。ある程度カクカク。"DOMからinline styleのleft/topを変更"よりFPSが高い。
- 衝突判定: 移動時に、自分を移動させる座標で行う
- ヒットテスト: 自分で覚えている座標で行う
DOMからinline styleのleft/topを変更
CSS Animationでleft/topを利用
- キャラクターの数が少ない(5個)場合: FPS不明。スムーズ
- キャラクターの数が多い(80個)場合: FPS不明。カクカク(DOMからinline styleのleft/topを変更とほぼ同じ)
- コンテンツ: http://ndruger.lolipop.jp/hatena/20110617/drawing_performance/drawing_performance.htm?type=css_animation&number=80
- CSS Animationで行っても、left / topを変更するとブラウザの重いが実行されるため、パフォーマンスに大きな変化はない。下回りで位置の補完とか無茶なことをしてたら別だが、してないと推測。
- 衝突判定: CSS Animationとは別に行う必要がある。
- ヒットテスト: リスナーを使うか、getComputedStyle() or 自分で時間から位置を計算して自力で行う
- 他
- 現状inline styleに対してDOMを使って指定できない。下記のようにstyle要素を書き換えてがんばる
- ループしてから減速するなどを1つのルールで書けないので、webkitAnimationEndでルールをすげ替える必要があり、その時にアニメーションがわずかに止まる感じになる。アニメーション中にユーザー操作で動きを変えるときもこの問題が起こる。
- 結構複雑なことをすると、Android / iPhone3G / iPhone4で動作の違い(バグ?)が発生したりする。
- フレーム毎にjavascriptの関数が呼ばれので、FPSを計算できない
- 複数のアニメーションの同期が難しい
CSS Animationでtranslate()を利用
- キャラクターの数が少ない(5個)場合: FPS不明。非常にスムーズ
- キャラクターの数が多い(80個)場合: FPS不明。非常にスムーズ
- コンテンツ: http://ndruger.lolipop.jp/hatena/20110617/drawing_performance/drawing_performance.htm?type=css_animation&use_css_transform=true&number=80
- translate()はレイアウトに影響しないので、ブラウザ内の処理がほとんどなくなり、非常にスムーズな動きになる。
- 衝突判定: CSS Animationとは別に行う必要がある。
- ヒットテスト: リスナーを使うか、自分で時間から位置を計算して自力で行う
- 他
他のテクニック
canvasでclearRect()を全体にせず、個々のキャラクターの範囲に対して行う
- キャラクターの数が少ない(5個)場合: 42FPS。全画面clearRect()よりFPSが高い
- キャラクターの数が多い(80個)場合: 16FPS。全画面clearRect()よりFPSが低い
- コンテンツ: http://ndruger.lolipop.jp/hatena/20110617/drawing_performance/drawing_performance.htm?type=canvas&use_min_clearrect=true&number=80
- 書き換える領域が全体に近くなり、clearRect()の複数回の処理の方が悪影響になるため。
動いてない奴の描画を防ぐためdirty flag等を利用する
- styleやcanvasに変更など非常に重い処理の前に、動かす必要のある物か判定して、不要なら変更しない。
postMessage()によるsetZeroTimeout()の利用
- setZeroTimeout()の参照元
- 内部的にタイマーで実装してないようなので、プラットホームごとに使っているタイマーの精度より早く呼ばれる可能性がある。
- コンテンツ: http://ndruger.lolipop.jp/hatena/20110617/drawing_performance/drawing_performance.htm?type=canvas&use_set_zero_timeout=true&number=5
- 個人的にはループで使うものではないと思う。
適切な選択
- スムーズさを優先するならば、可能な限りCSS Animationでtranslate()を利用するが、やりたいことが制限にひっかかった場合、その制限に応じて、他の奴を選択する