Webページレンダリングについて知っておくべきこと: ピクセルは高コスト

Web開発者として、ユーザの画面表示にピクセルがどのように関わるのかということは知っておくべきでしょう。知ることが目的なわけではなく、効率性のため画面表示を最適化する際にその知識が必要となってくるからです。

先日、「フロントエンド開発者がWebページレンダリングで知っておくべきこと」を読んだのですが、重要なポイントを外してしまっている印象を受けました。その記事で強調されていたのは、CSSセレクタのマッチング、レイアウト(FirefoxのようなGeckoベースのブラウザではリフロー)、そしてレイアウトスラッシングという名でも知られる強制同期レイアウトに注意することです。確かに、これらは気をつけたほうがいいことだとは思いますが、私としては、ページレンダリングについて開発者が知っておくべきことをその記事ですべてカバーしていたとは思えません。大抵の場合、Web開発者は60fpsの表示を目指しますが、そのためにはブラウザの動きや最適化について理解することが必要です。

私は今年のGoogle I/Oでセッションを行い、パイプラインについて詳細に踏み込んでお話ししました。もし記事を読むよりもビデオを見るほうがよければ、こちらのビデオをご覧ください。

パイプラインのレンダリングの話までビデオをスキップしたい方は、16分経過した辺りを見てください。

Webページレンダリングについて開発者が知っておくべきこと

開発者として、気にしておくべき領域が4つあります。

1. スタイルの再計算

まずはセレクタマッチングにつきもののスタイルの再計算です。そして、DOMのどの部分にどのスタイルが適用されるのかについても理解しましょう。

CSSコードとエレメントを結びつけているブラウザ

通常この処理はDOMツリー配下に無数のエレメントが存在し、ツリー全体に影響を及ぼす変更(たとえば、bodyエレメントは全ての子エレメントに影響を及ぼしますが、そのbodyエレメントにクラスを追加するような変更)を行わないかぎりは、非常に高速に動きます。

多くの場合、セレクタマッチングの最適化を行えば、最低限の結果を得られます。ただし、次に起こることを考えない場合に限りますが…。

2. レイアウト

いったんDOMに適用するスタイルが決まると、レンダーツリーが生成されます。レンダーツリーは以下の図のようなものです。本質的に何かが足りないDOMツリーのようにも見えますし(でも欠けているのは描く必要のないものです)、他のもの(擬似的なエレメント)が加わっているようにも見えますね。

レンダーツリー:私たちが構築する必要のあるもの

レイアウトでは、私たちはページの配置を考えます。すなわち、各エレメントをページにどう配置するかといったことです。ここで計算コストが高くなり始めます。エレメントの配置は不確かなところが多くて、1つのエレメントはたいてい他のエレメントと関連していることが理由です。bodyタグのディメンションを変えるとしたら、その配下にあるエレメントも影響を受けることになるでしょう。

また、面白くなってくるのもここからです。たとえばページの配置を変更するレイアウトプロパティ(padding、margin、position、font-size、border)を何か1つ変更したとします。すると何が起きるでしょうか。私が作成した以下のビデオをご覧ください。エレメントの配置を変更したときの、ChromeデベロッパーツールのTimelineを記録したものです。ここでは、高さを変更しています。

注目したいのは、バーが60fpsラインをはるかに超えてしまっていることと、各フレームで配置の変更を把握するために~23msくらいかかってしまうことです。これは、アニメーションがJavaScriptでトリガーされる場合でもCSSでトリガーされる場合でも同じで、レイアウト作業はどちらの場合も必要です。60fpsのパフォーマンスの達成を目指しているときは、~16msで何もかもを終わらせることができますが、それを1つのタスクだけで超えてしまいました。やれやれ。

ここで大事な情報ですが、強制同期レイアウト、つまりレイアウトスラッシングは回避することを強くお勧めします。ブラウザが、不要なレイアウト/リフローを行うことになるからです。これでは事態がさらに悪化するだけです。

そうは言っても、このビデオを見ておそらく皆さんも気づいたのではないでしょうか。レイアウトには~23msかかっていますが、変更したピクセルのペイントには何と~600msもかかっています。

3. ペイント

さて、ピクセルを塗りつぶす必要があるエレメントがどこにあるか分かりました。それこそが大部分の時間を費やすことになる場所です。ペイントをトリガーすれば、あなたもそれを実感するでしょう。そう、あなたのユーザは間違いなく、あなたのサイトが必死に頑張っているのを感じるはずです。以下のビデオは別の例です。エレメントの1つをグレーからきれいなピンク色に変更して、ペイントをトリガーしています(ここではレイアウトの変更はありません)。

ペイントをトリガーするプロパティの種類は、color、background、shadowですが、実際は、レイアウトプロパティを何か1つ変更するとペイントをトリガーすることになります。ページの配置が変更され、ピクセルが崩れるので修正する必要があるのです。

ペイントを終了すると、いよいよ最後のパートです。

4. 合成

デフォルトで、エレメントはメモリ内の1つのレイヤでペイントされます。これは、私たち開発者にとってPhotoshopのカンバスと同じようなものと考えることができます。ピクセルをブラシで修正したら、さあ大変、もう元のピクセルに戻すことはできません(Photoshopには「取り消し」があるんですけどね…笑)。

この問題を避けるために、Photoshopやほかのアート系ソフトと非常に似通った方法で、エレメントを取り出して別のレイヤに分離することがあります。このレイヤを、コンポジタ・レイヤと言います。こうすれば、あるエレメントをペイントしなければならないとき、他の部分をペイントしなくてすみます。私は以前に、エレメントをそのレイヤに”昇格”させるために現在使用しているルールについて書きましたが、まったく新しい方法があるので、近いうちにご紹介します! :-O

芸術家…ではなく私の印象でコンポジタ・レイヤをイメージ化したもの。

レイヤがそれぞれ完成したら、すべてまたギュッとくっつけます。合成です。

パフォーマンスのボトルネックを回避する

しばらく前にポール・アイリッシュと共著でHTML5Rocksに記事を書きました。アニメーションの高速化や、パイプラインを活用して60fpsを達成する方法などについて説明しています。つまり、レイアウトやペイントではなく、合成にトリガーをかけるプロパティを変更するだけでいいということです。


レイアウトやペイントにトリガーをかけることなく変更できるプロパティ。

これはよくあるtransformのリストです。Opacityも含まれていますが、理論的には、エレメントさえ同一のコンポジタ・レイヤ上に置いておけば問題なく機能するはずです。Will-changeは出来立てほやほやの方法です。このプロパティを使用すると、ChromeやFirefox が、opacityやtransformのようなある種のキーワードの適切な最適化を内部で行ってくれます。これら2つのキーワードの場合、少なくともChromeではコンポジタ・レイヤを作成してくれます。

Will-changeになじみがないという人は、最近サラ・スエイダンdev.Operaで記事を書いていますので参考にしてみてください。最新バージョンのChromeも含めて自分の作業には使えないという人はwebkit-backface-visibilityなどの昔ながらのハック法に頼るのでしょうか。こっそりと使っている永遠のお気に入り、webkit-transformや、translateZ(0) (ohai null transformレイヤハックとも言います)などもありますが、結局、自己流になってしまいますよ。

[訳者注] サラ・スエイダンさんのdev.Operaでの記事にはPOSTDの翻訳があります。

合成の活用を心がければ、超軽いフレームと60fpsを実現できるでしょう。

ご参考までに、透明なフレームが見える場合、まだ60fpsを達成していません。たとえ作業が16msより早く完了していたとしてもです。このような場合はタイムラインバーを空のスペースで埋めます。スペースですよ。

ルールではなくツール、そして明るい未来

状況は常に変化しています。今まで説明してきたパイプラインについても例外ではありません。でも喜んでください。開発ツールは常に最新かつ適切なものが用意されています。ツールと親密な時間を過ごすのに慣れていなかったら、これからぜひ慣れてください。きっとツールも愛し返してくれますよ。

すばらしいことに、高速化は進んできています。今年のGoogle I/OではAndroid上のChromeで、高速で流れるような60fpsのアニメーションのデモを見せることができました。高速化は常にChromeのチームにとっての重要課題です。もちろん他のブラウザベンダーにとっても、そうでしょうけどね。

いつの日か、「まともな」コードさえ書けばあらゆること、すなわちレイアウトのトリガー、ペイントや他のことなどもブラウザが勝手にやってくれる日がくるといいなと思います。でもその日までは、フロントエンド開発者はWebページのレンダリングに関してここに書いてあることだけ知っておいてもらえれば大丈夫です。