JavaScript

ブラウザで大きな画像の描画をキャッシュするプリレンダリング

More than 3 years have passed since last update.

巨大な画像をスムーズに表示する素敵なバッドノウハウの共有です。

大きい画像を事前にプリロードしておくことは普通にやっていると思いますが、大きい画像ってのは描画コストも当然に大きいです。DOMに配置してから、ほんのわずかですがタイムラグが発生します。すばやく画像を切り替えるような演出だとチラついたりしちゃって興ざめですね。そういった時に有効なのがプリレンダリングです。

ブラウザにそんなものあるのかっていうと、たぶんないです。勝手にそう呼んでるだけで、ようするに事前に画像を描画させてキャッシュしておこうということです。

pre-render.png

体型は変わりません


デモ

ごく普通にプリロードのみ実装したものから。


プリレンダリングなし

http://nenji.ru/_/prerender/pre-load.html

さっそうとモデルウォークしてるミクさんがいますね。さすが40コマ、ぬるぬるです。

ちなみに画像サイズは 450×450 なので、高さ 18450px の画像てことになりますね。おそろしい。

リンク先の walk ボタンを押下すると run アニメーションに切り替わるのですが、切り替わる瞬間にチラつく環境がほとんどだと思います。

none-prerender.png

たとえプリロードしていても、これだけ大きな画像だと描画に時間がかかります。

すばやく表示を切り替えなければならないアクションゲームならば、かなり致命的なラグだということが分かってもらえるかと思います。


プリレンダリングあり

http://nenji.ru/_/prerender/pre-prerender.html

プリレンダリング済みのコマアニメです。

run-walk.png

おそらくこちらは切り替えの瞬間にチラつきはなかったと思います。

なにをやっているのか簡単に解説します。


仕組み

プリレンダリングとかいって大げさな感じですが仕組みは簡単です。

画像をDOMに追加し、画面上に表示させて再生させればいいのです。

とはいえ、プリレンダリング自体がまる見えでいい場合ってのはほぼないですよね。

これを隠そうとすると意外に大変なんです。

display:none してしまうと、そもそもレンダリングされません。

visible:hidden も、opacity:0 もやはりだめです。

position:absolute で画面外にすっ飛ばしても同様です。

ブラウザの仕組みまでは理解してないので確証ないですが、実際に画面上に存在していないと画像が描画されないのでしょうね。


z-index で隠す

有効なのは、z-index でなにかのレイヤーの裏側に配置した場合です。

よくやる方法としては、背景を表示している最深部のレイヤーの裏側に隠しておきます。

layer.png

見えないように配置しながら、コマアニメをしっかり表示してキャッシュさせるのです。

では、この表示しきった状態をどうやって知るのかというと、残念ながらまっとうな方法はありません。

画像描画完了イベントなんて存在しませんが、そうはいってもどうにかして検知しないといけないです。

そこでまた、まったくおすすめしないバッドノウハウを紹介します。


画像描画完了イベントなんて存在しない

大きな画像を描画している間は、FPSが非常におちます。CPUに負荷がかかるんでしょうね。

CPU負荷があがると、JSのタイマーも影響をうけます。

このタイマーの間隔を、タイムスタンプで比較して、CPU負荷さがったかなーって判断します。

さきほどのミクさんのコマアニメのやつで console.log を開いてください。

出力されている数字は、タイマーループ間の時間(ms)です。

画像を裏側でレンダリングさせている間は、FPSが大きく落ちてますね。

これを目標とするFPSに落ち着いたら、描画完了とみなしています。

console-log.png

バッドノウハウですねー。

まったく保証できない方法です。

でもどうにかこの方法で切り抜けています。

まっとうな代替手段あるなら、こんな方法はかなぐり捨てたいです。

それでは以上、よろしくお願いします。