Webサイトのパフォーマンス改善を行うためにGPU処理を取り入れてみた

ごきげんよう。フーミンです。
最近のサイトはいたるところにアニメーションする要素が増えてきましたね。
制作ツールや学習環境が充実してきたことによって、実装が簡単になってきたことも要因の一つでしょうか。

実装が簡単になった分、
「アニメーション簡単で楽しい!どんどんアニメーションさせてイケてるサイト作るぜ〜!」
と、なにも考えずに何でもかんでもアニメーションさせていたら、

「なんか重いなぁ・・・動きもカクカクしてるなぁ・・・」

なんてことになったことありませんか?

今回は、もっさり重くなってしまったサイトのパフォーマンスを改善するために、原因の追求と対策について、簡単に説明していこうと思います。

パフォーマンスの改善について

まず、ひとことに「パフォーマンスの改善」といっても、

  • ネットワークパフォーマンスの改善(HTTPリクエストを減らす など)
  • JavaScript & CSSパフォーマンスの改善(JavaScript & CSSの圧縮 など)
  • レンダリングパフォーマンスの改善(描画の最適化 など)

と、やるべきことはたくさんあるのですが、今回取り上げるのは、アニメーションを多用したビュンビュン動く系サイトに効果的なレンダリングパフォーマンスの改善方法です。

ブラウザがWebページを表示するには、
HTMLの解析 → フロー(レイアウト) → ペイント(描画)
というステップを踏みます。

例えば、jQueryのanimate();を使って次のようなアニメーション処理を書いたとします。

$('#object').animate({ left: 100; });

この「#objectをleftへ100px動かす」アニメーションは、フレームごとにリペイント(再描画)を繰り返しながら動いているように見せているだけなので、アニメーションとしての動きは滑らかではありません。

このリペイントが発生するたびにCPUに負荷がかかり、レンダリング速度が低下するため、滑らかなアニメーションを実現するためには、極力無駄なリペイントを減らしていかなければなりません。

原因(無駄なリペイント)を見つける

なにはともあれ、まずはサイトのどの部分にリペイントが発生しているか確認してみましょう。
Google ChromeのDeveloper Toolsを使うとペイントの様子を視覚的に確認できるので、これを使ってみましょう。

DEMO 1

1

「Rendering」タブの「Show paint rectangles」にチェックを入れ、確認したいページで更新ボタンを押してみましょう。
※「Rendering」タブが表示されていない場合は、Developer Toolsを開いてからキーボードの「Esc」ボタンを押してみましょう。

2

一瞬ですがブラウザ画面上で緑色に表示された部分があると思います。
その緑色の部分がペイントにあたります。

3

さらにそのままアニメーション部分を見ていきましょう。
緑色がチカチカと何度も表示されていると思います。
これがリペイントにあたります。

この、

  • リペイントの回数が多い
  • 処理時間が長い
  • 面積(領域)が大きい

部分が今回取り除くべき問題になります。

アニメーションにGPU処理を取り入れる

リペイントの負荷を減らすために、GPUを使う方法があります。

GPUを使うことによって、DOMエレメントをフレームごとにリペイントしてアニメーションさせるのではなく、個別のGPUレイヤーとしてアニメーションさせることが可能になります。

※さらに詳しい仕組みが知りたい方はこちらの参考記事へ
Web描画パフォーマンスの改善

DEMO 2

4

それによりリペイントが発生せずCPUに負荷がかからなくなるので、レンダリングの高速化につながるというわけです。

ただし、すべてのアニメーションをGPUで処理できるわけではありません。
GPUで処理できるアニメーションは、

  • 移動   :translate
  • 拡大・縮小:scale
  • 回転   :rorate
  • 透明度  :opacity

の変更だけなので、アニメーションを作る際には少し注意が必要です。

アニメーションにGPU処理を取り入れるには、3D Transform関連のCSSプロパティを持たせる必要があります。

「3D Transform関連のCSSプロパティを持っている場合、その要素が自動的にGPUレイヤーになる」

という仕組みを利用し、hack的な方法ですが、GPU処理を取り入れたい要素に以下のCSSプロパティを追加し、強制的にその要素をGPUレイヤー化させます。

backface-visibility: hidden;

※必要に応じてベンダープレフィックスを追加

CSS の backface-visibility プロパティは要素の裏面がユーザに面したときに、裏面を可視にするかどうかを決めます。要素の裏面は常に透明な背景を持ち、可視ならば表面の鏡像を表示します。
(2 つの要素を交互に使って)カードを裏返す効果を使う場合のように、要素の裏面から表面が見えてほしくない場合があります。
このプロパティは 遠近感 (perspective) のない 2D の 変換 (transforms) には効果を及ぼしません。
MDN

これでアニメーションにGPU処理が取り入れられたわけですが、
レンダリングが早くなるからといって、なんでもかんでもGPUレイヤーにすれば良いというわけではありません。

GPUレイヤーは生成するたびにメモリ(VRAM)を消費していきます。
メモリは有限なので足りなくなると、CPUで処理しなくてはならない部分が増えてしまい、逆に処理が遅くなってしまうこともあります。
GPUレイヤーにする箇所はリペイントの多い箇所に留めるようにしましょう。

5

※メモリの使用量は「Rendering」タブの「Show FPS meter」または「Enable continuous page repainting」で確認することができます。

まとめ

アニメーションに関するパフォーマンス改善を行うには、

  1. レンダリングの仕組みを理解する
  2. GPU処理を取り入れる
  3. 無駄なリペイントを減らす
  4. GPU処理に頼り過ぎない

の4つが重要です。

アニメーションに限らず、ものごとの仕組みを理解すれば、どこに問題があり、なにをすればよいのかが直ぐに見えてくると思います。
小手先の技術だけではなく、見えない部分の理解を深めることも大切ですね。

それではまた次回。
ごきげんよう。