問題です。とあるアニメーションデータの各フレームを静止画として書き出した200枚前後のPNGファイルがあったとします。この連番の静止画を秒間30コマでアニメーションさせてください。さあ、どうしますか?
この問題にたいして、自作の画像生成ツールを作って解決したというお話です。
先日の案件にて、そんなお仕事がわたしに課せられました。そのときの課題と制約は以下のような感じでした。
- IE8以上のブラウザに対応すること
- iOSデバイスで専用の再生画面を表示することなく再生できること
- 閲覧時、画面内にスクロールインしたタイミングで自動再生すること
- 任意のタイミングで再生開始・停止できること
- 可能な限りファイルサイズを削減すること
IE8対応が必要なので、Canvasは使えません。<video>タグはiOSで自動再生できないし、再生ボタンをタップすると画面が変わってしまうのでダメ。好きなタイミングで再生・停止ができないからGIFアニメーションもNG。そうすると、<img>タグとか、CSSのbackground-imageを使って、JavaScriptでなんとか頑張るしかありません。
問題になるのはファイル数とファイルサイズです。<img>タグをぱっぱか切り替えてアニメーションをさせるということは、アニメーションのフレーム数ぶんだけファイルが必要になります。つまり、200枚×50KB = 10MBのファイルを読み込ませる。ちょっとこれは、ないですね。
活用すべきは偉大なる先人たちの知恵です。世の中にはCSSスプライトという技術がありました。CSSスプライトを使えばリクエスト数を劇的に減らすことができそうです。しかし合計ファイルサイズはほとんど減らないでしょう。合計10MBだったものが、9MBになってもそれほどうれしくありません。
追い詰められたわたしは知恵を絞って考えました。アニメーションといっても、全編通して画面全体がずーっと動き続けているわけではありません。間をあけるために数秒間止まることもあるし、画面内の小さな領域がチマチマ動いているだけのこともある。連番の静止画データはそれらの重複したデータを含んで全部持ってしまっているから、これをやりくりすればファイルサイズを削減できるのではないか。
つまり、フレーム間の差分だけを画像として保持しておき、再生するときにそれを組み合わせて動かせば、元のアニメーションデータが再現できるはず、と考えました。
そこでツールを作りました(汎用性は全然ないものなのであえてフィーチャーしませんが)。このツールは複数の画像ファイルを入力として、差分抽出をした後のスプライトシートを出力してくれます。大まかに以下のような手順で使用します。
- 連番の画像ファイル(縦横サイズはすべて一緒)を用意する
- ツールに画像ファイルをすべてぶっこむ
- キーフレームをクリックで選択する
- 生成完了まで待機する
キーフレームというのは、一般的な映像圧縮でいうところのキーフレームと考え方は同じものです。今回のファイルサイズ削減アプローチは、キーフレームと、そのキーフレームからの差分データをレイヤー的に重ねることで、特定のフレームの見え方を再現するというものです。場面が転換した瞬間のフレームや、アニメーションが停止している区間の最初のフレームを指定しておくと、結果的なファイルサイズが抑えられるというあんばいです。
アニメーションのをフレームごとに書き出した連番のファイルは合計で11.8MBでしたが、このツールに通すとなんと1MBまで縮んだ!
上述のサンプルのようなアニメーションだったら、背景と前景の物体を分離して、前景だけをjQueryとかでごりごり動かすのがもっともファイルサイズ的な効率はよくなるはずです。が、元データに手を入れる権限がないとかで綺麗に分離できないとか、これ系のアニメーションを大量に横展開しなきゃいけないとかだと、すべてのアニメーションに手動で職人技を施すわけにはいきません。変更にも弱いし。今回はそのような条件が重なったために、こういうファイル削減アプローチをとることにしたわけです。
ツールを使っている場面のスクリーンショットです。この画面でキーフレームを選択する。
こんな感じで結果が出る。この画像をローカルに保存するところは手動。
株式会社ネコメシでは、業務遂行上の問題に対してあらゆる手段を講じる意欲のある人材を募集しております。