ウェブサイトアニメーションパフォーマンス

画像







条件付きブートストラップのフレームワークを超えるサイトを開発する場合、アニメーションのパフォーマンスに関して遅かれ早かれ疑問が生じます。 これらは、Awwwards、FWA、CSS Design Awardsなどのカタログに含まれるデザインサイトなどで特に重要です。 ただし、多くの場合、アニメーションの作成とその後の最適化のタスクは、必要に応じて、どこから始めてもわからない経験の浅い開発者に任されています。 通常、これはすべて、使用が不可能な抑制サイト、およびそのようなプロジェクトのクラス全体に対するその後の否定的な態度につながります。 この記事では、許容可能なアニメーションパフォーマンスの境界がどこにあるのか、一般的なボトルネックは何なのか、そもそも開発者ツールのどこを調べるべきなのかを理解しようとします。







ちょっとした発言:この記事は初心者開発者向けであり、アニメーションの最適化への一般的なアプローチを示すことを目的としているため、多くのことは単純な学術的形式ではなく簡略化されています。







ブラウザがページを表示する方法



まず、ブラウザにページの現在の状態が表示されたときに何が起こるかを理解しておくと役立ちます。 4つの主な手順があります。







  1. スタイルの計算(ブラウザーはCSSセレクターを解析し、適用するスタイルを決定します)
  2. レイアウトの作成(ページレイアウトは実際に形成されます)
  3. ペイント(後続のレンダリング用の要素のピクセル表現を作成します)
  4. レイヤー構成(ブラウザーはすべてをまとめて収集し、画面に表示します)


さらに、ブラウザは常にこの順序で動作し、最後まで進みます。 ロード後にページが最初に表示されると、4つのステップすべてが実行されます。 将来、アクションの1つが実行される可能性がありますが、同時にすべてのアクションが実行されます。 しかし、前のものではありません。







これらの各ステップのボトルネックについてさらに検討し、一見したところ愚かな質問を1つ質問します。







それが遅くなるかどうか、それは問題です...



非常に頻繁に、明らかに遅いWebサイトで何もしていない人に会って、「私のページの速度は100ポイントになります。すべては問題ありません」と言うことができます。 またはその逆に、うまく機能しているサイトでは、ある種のアルゴリズムがいくつかの不可解なメトリックに基づいて非効率的に機能するため、人々は長い間、ある種の最適化に取り組んできました。 しかし、これらの両極端の間は常識の真ん中にあるはずです。







画像







禅を知る アニメーションを最適化する必要があるかどうかを理解するには、深い哲学的思考を実現する必要があります。







サイトが遅いことがわかると、遅いことを意味します。 サイトの速度が低下していなければ、速度は低下していません。

何らかの理由で、多くの人がこの声明を非常に愚かだと思っていますが、そうですか? エンドユーザーにとって、パフォーマンスとは、ある種のメトリックではなく、厳密な数学的正当化を伴う理想的なアルゴリズムでもありません。 彼にとって、パフォーマンスは2つのことの1つです。パフォーマンスが低下するか、低下しないかです。







彼はどのようにこれを決定しますか? モニターの後ろで多くの時間を過ごす人の目は、fpsの低下に鋭く反応し始めます。 これは奇妙な不快感を引き起こします。 したがって、開発者としての私たちの仕事は、地盤沈下を防ぐことです。 ユーザーはブラウザが60 fpsで動作するのを見ることに慣れていますか? それでは、すべてが同じになるようにすべてを行っています。 中程度の鉄のラップトップを持って見て。 60fpsよりはるかに少ない-最適化されています。 私たちは約60を見る-何も触れないでください。 ユーザーはとにかく違いに気付かないでしょう、そして最適化のために最適化に多くの時間を費やします。







最適化のために最適化を行わないでください。


16.5ms



fpsの観点から自分を表現するのは便利ではないので、ミリ秒に移りましょう。 1000ms / 60fpsの単純な分割により、フレームごとに約16.5msの時間が得られます。







これはどういう意味ですか? 16.5msの間、ブラウザは上記の手順に従って、ページの現在の状態をアニメーションで表示し、同時に、他のスクリプトの作業、サーバーとの通信などのためにリソースを保持する必要があります。 ページの現在の状態を表示するのにより多くの時間が費やされる場合、目を通してラグを確認します。 約16msの場合、沈下はありませんが、鉄の負荷が非常に高くなり、クーラーが鳴り、電話が暖まる可能性があります。 したがって、1つのフレームのレンダリングが時間内にこの値に近づかないようにする必要があります。パフォーマンスに余裕があるように、さらに良いのは10ms以下です。 テストは常に中央のハードウェアで実行されることを忘れないでください。たとえば、次の例では、スクリーンショットはグラフィックが統合されたPentium Silverで撮影されます。







ユーザーが持つ可能性が高いハードウェアでテストを実行します。 職場のテーブルの下にトップエンドプロセッサとマイニングファームがある場合、すべてが正常に機能しますが、予算のラップトップを使用するユーザーは非常に悲しくなります。

あなたの良い目と直感だけに頼らないために、少なくとも基本的なレベルで開発者ツールを習得することは有用です。 これらは正確なパフォーマンスデータを提供するだけでなく、すべてがうまく機能しない場合に問題を探す場所を教えてくれます。







Google Chromeの開発者ツール



多くのコーダーは、Linuxコンソールよりもブラウザーの開発者ツールにしばしば打たれます。 しかし実際には、恐れることは何もありません。 はい、多くのボタンがありますが、それらは問題を解決するために冗長です。 アニメーションをどうするか、そして何かをする必要があるかどうかを理解するために、まず最初に注意を払う価値がある場所を見ていきましょう。







パフォーマンスに関しては、ほとんどの時間をパフォーマンスタブで過ごし、同じボタンを押します。







画像







Ctrl-Eショートカットまたは左側の丸いボタンは、何が起こっているのかを記録し始め、停止します。 結果はここに表示されます。 ブラウザはたくさんのことを書きますが、何度も読むよりも一度見たほうがいいので、何らかのアニメーションを使って見てみましょう。 まずはシンプルなCSSアニメーションにします。 全画面で開くと、目立った紙詰まりで動作することがわかります。









全画面モードで数秒間記録し、そこで何が起こるかを確認します。







画像







ブラウザは、実行するすべてを記録します。 ウィンドウの上部にfpsチャートが表示されます。 ページでの作業中に異常に速度が低下し始めた場合に、異常の検出に簡単に使用できます。 マウスでチャートをクリックして横に引くか、ホイールを回すと、この時間範囲を選択でき、その詳細情報が下に表示されます。 この単純な例では、異常はありませんが、すべてが非常に均等に機能するわけではないことは明らかです。







すぐにFrames行に注意してください。各行に費やされた時間に関する情報が含まれています。 この時間は絶えずジャンプし、16msを大幅に超えていることがわかります(実際の例では、このアニメーションを少し改善します)。







次に、負荷がさまざまな色で表示される複数の行が表示されます。ブラウザがさまざまな種類のアクティビティに費やした時間を確認できます。 アニメーションは均一で、同じ操作が各フレームに対して実行され、紫と緑で示されています。 マウスを色付きのブロックの上に置くと、最初に言及したポイントを処理していることが明らかになります。 スタイルの再計算レイヤーツリーの更新は紫色で、 ペイント レイヤー合成レイヤーは緑色です。







別のアニメーションを検討してください。 今回はスクリプトを使用します-単純なノイズジェネレーター。 これはかなり例示的な例ですが、設計の観点からは興味がありません。









スクリプトの実行を表示する黄色のブロックが追加されていることに気付くかもしれません。 多くの関数呼び出しがある場合、呼び出しごとにさらに1ブロックが追加されます。サイズによって「最も重い」関数を簡単に見つけることができます。おそらく、最適化を開始する価値があります。







画像







この例では、1つのフレームに費やされる時間は約80ミリ秒変動します。 しかし、そこにあるものは、肉眼でもすべてがクラッシュする様子をはっきりと見ることができます。 以下の要約セクションを見ると、スクリプトが最も時間がかかることがわかります。 それらと比較して、 レンダリングペイントは無視できるエラーのように見えます。 もちろん、必ずしもそうではありませんが、頻繁に起こります。







関数呼び出しとしてマークされているブロックをクリックすると、スクリプトコード内の関数へのリンクが表示されます。 確認すると、この例では、画面上のすべてのピクセルが循環していることがわかります。 シェーダーでこのようなタスクを実行する方が論理的である場合、パフォーマンスは何倍も向上します。 しかし、実際の例で見ていきます。







どうすれば...



ブラウザーでページの現在の状態を表示する際の手順と、最も時間がかかっているページを確認する手順を学びました。 特定のステップが多くのリソースを必要とするようになる最も一般的な理由を知り、特定のケースで何をすべきかについてのヒントをいくつか示します。







スタイル計算



このステップですでに問題が発生していることがわかった場合、ほとんどの場合、ポイントはアニメーション内にありませんが、ページ上の要素が多すぎるという事実です。 設計サイトでは、これは非常にまれです。通常、このような問題は、数千の要素を持つ大きなテーブルのサテライトです。







ページ上の要素の数を減らし、レイアウトを簡素化します。 ラッパーを使用してコードの一部を繰り返すことに特に注意してください。コードは削除できる可能性があります。

最初に関連する2番目の理由は、複雑なCSSセレクターです。 小さいページで、深いネスト、隣接する要素とのトリッキーなハッキングなどを使用することが非常に可能である場合、本当に大きいページでは、これはすべてパフォーマンスの低下につながる可能性があります。







CSSセレクターを簡素化し、BEMを使用します。


レイアウト作成



このアイテムはすでにデザインとアニメーションに近いので、ここから面白いことが始まります。 理解することが最初に重要なことは、レイアウト全体が形成されることです。 何かを変えると、新たに形成されます。 このため、大きなページでの小さな変更でも、このステップで顕著な遅延を引き起こす可能性があります。







アニメーションを作成する際に私たちを導く主なルールは、いかなる場合でもレイアウトの再構築を許可しないことです。 したがって、通常は最適化を試みません(特に機会はありません)。つまり、回避しようとします。







レイアウトの再構築を引き起こす可能性のある多くのプロパティがあります。たとえば、 csstriggers.comは悪くない、インターネット上のリストを見つけることができます。 他のアニメーションよりも頻繁にプロパティを見つけることができます:







display position / top / left / right / bottom width / height padding / margin border font-size / font-weight / line-height ...
      
      





これらのプロパティはすべて、要素の幾何学的特性を表す1つのものに統一されていることに気付くかもしれません。表示パラメータ、サイズ、物理的位置です。 したがって、それらすべてを記憶するのではなく、それらが何を指しているのかを覚えておいてください。







要素の幾何学的プロパティを変更しないでください;変換と不透明度をよりよく使用してください。

それとは別に、要素の背景を変更するとこのステップに戻ることにも注意する価値があります。 彼らは常にこれを忘れているので、別の推奨事項で強調します:







背景要素を変更しないでください。

一部のブラウザ( Firefoxで指を突くことはありません )特に単位時間あたり複数のアニメーションが実行される場合、変換を伴うCSSアニメーションの典型的な遅れが現れることがあります。 外見的には、これは彼女の作品の一時停止としてだけでなく、アニメーションの最初の部分の「内訳」としても見えるかもしれません。 ブラウザは常に何かを再計算しているようです。 この動作は、ほとんどの場合、 backface-visibilityプロパティを使用して修正されます。







可能であれば、アニメーション化された要素にbackface-visibility:hiddenを追加します。

また、レイアウトの再構築は、スクリプトからの要素の呼び出しによって引き起こされます。 さらに、これはCSSを直接​​変更する必要はなく、要素の一部のプロパティとメソッドにアピールすることもできます。 最も一般的なものは次のとおりです。







 offset*** client*** inner*** scroll***
      
      





アニメーションでは、それらに注意する必要があります。 多数の要素についてこれらのプロパティとメソッドを参照し始めると、そのたびにレイアウトが再構築されます。







ループ内の個々の要素について、前述のプロパティとメソッドを参照することは避けてください。


ペイントとレイヤー構成



これら2つのステップを一緒に検討します。 それらはある程度関連しており、通常、一方に問題がある場合、もう一方にも問題があります。 これらの手順をスキップして回避することは機能しないため、何らかの方法で最適化を試みています。







ブラウザはページのピクセル画像を全体ではなく部分的に-レイヤーで準備します。 たくさんあるかもしれません。 各レイヤーは、それ自体が存在するかのように存在し、残りに影響を与えません。これにより、一部のCSSハックの基礎が作成されます。 しかし、私たちはそれらについてもう一度話します。 次に、これらのレイヤーから最終画像が収集されます。 アニメーションのコンテキストでは、アニメーション要素を別のレイヤーに配置して、それらの変更が周囲のすべてに影響しないようにすることが非常に便利です。 要素の内容は小さいことが望ましい。 これを行うには、 will-changeプロパティを使用するか、以前のようにtransform:translateZ(0)を使用します。 覚えておくべき唯一のことは、レイヤーの数を無制限に増やすことはできないということです。 ある時点で、これはトリックを果たし、反対のパフォーマンスが低下します。 したがって、2つのヒントがあります。







will-changeまたはtransform:translateZ(0)を使用して、アニメーション化された要素を別のレイヤーに移動します。

しかし同時に







このビジネスで無理をしないでください。 悪化していないことを開発者ツールで確認してください。

非常に多くの場合、深刻な問題は、何らかの方法で要素の画像を変換するフィルターによって引き起こされます。 ぼかしを使用した単純なCSSフィルターやSVGとの混乱したオプションを使用できますが、効果は同じになり、パフォーマンスが著しく低下します。







複雑なフィルターを使用しないでください。 それでも意図した効果が必要な場合は、WebGLに実装することを検討してください。


これらのヒントはどのように機能しますか?



それらは機能しますが、奇跡を期待する必要はありません。 ネット上では、初心者が「意志の変更を加えましたが、何も変わっていません」と言うことがあります。 通常、これは、主な問題が別の場所にあったことを意味し、この手法は、気付かれないほど生産性のわずかな増加をもたらしました。 そのため、開発者のツールを使用してボトルネックがどこにあるかを正確に把握し、正常に機能しているものを最適化するための時間と労力を無駄にしないことが重要です。







これらすべてから、ページのレンダリングに影響を与える方法は多くなく、それらの効果は必ずしも重要ではないと結論付けることができます。 これらのトリックは特効薬ではなく、アニメーションを磨くために必要です。 パフォーマンスが非常に低いサイトを見ると、ほとんどの場合、私たち自身のスクリプトが原因であり、ブラウザーの腸のどこかでのCSS解析の不可解な問題ではないことに気付くでしょう。







スクリプト...



抑制アニメーションの問題が最も頻繁に発生する場所を知っていますか(私の観察によると)。 この開発アプローチから:







画像







馬鹿げているように聞こえますが、そうです。 何が起こっているのかを理解することなく、明らかにどこかから完全にコピーされたソリューションが常にあります。 コードの半分を削除しても、すべてが機能し続ける場合があります。 多くの場合、SOまたはToasterに対する回答のコードは、本番用ではありません。 これは明らかなはずです。 彼はアイデアを示し、質問に答えますが、特定のタスクに最適な最終オプションではありません。







すでにコピーしている場合は、少なくとも不必要なアクションのコードを見てください。


RequestAnimationFrame



彼らはよくこの方法について話し、アニメーションでsetTimeout / setIntervalの代わりに使用することを推奨します 。 これは理にかなっています。これらのメソッドは、ブラウザが再描画するフレームと同期が取れない傾向があり、小さな遅延が発生するためです。 しかし、2つのポイントがあります。







まず、ページで複数の要素がアニメーション化され、requestAnimationFrameを何度も呼び出すと、fpsが急激に沈下します。 理論的には、これはすべきではありませんが、実際には、すべてがそのように起こります。 ここでテストに慣れることができます







すべてのアニメーションコールバックを1つのrequestAnimationFrameに結合します。

2番目のポイントは、おそらくキャンバスを使用して重いアニメーションをすでに持っている状況に関連している可能性が高く、おそらくキャンバスを使用するか、またはやり直す時間がないため、次のことが起こります: しかし、現在の状態を計算するには多くのリソースが必要です。この図を見ると、アニメーションはスムーズに、美しく、2N秒、さらには3N秒で動作します。 その結果、すべてがすっごく知覚されます。 この動作を何らかの方法で修正するために、すべての推奨事項に反することができます。それにより、 setInterval / setTimeoutを使用し、アニメーション化された要素の状態を抽象フレームではなく物理時間にバインドします。 その結果、fpsは正式に減少しますが、生産性が向上するという心理的な影響があります。







極端に遅いアニメーションの場合、setInterval / setTimeoutを優先してrequestAnimationFrameを拒否することは理にかなっています。


キャンバスとシェーダー



非標準サイトのアニメーションの大部分は、キャンバスに関連しています。 これは理解できることであり、CSSは限られたものですが、ここではデザイナーの空想を実現できます。 ただし、通常の2Dキャンバスは最も生産的な技術とはほど遠いことに留意する必要があります。 その上に多くの要素を描画したり、ピクセルを直接操作したりすると、fpsが沈んでいるという事実にすぐに気付くか、または突然ペイントレイヤーの合成にかなりの時間がかかるようになります。 この問題は、例で明確に確認できます。









ブラウザの機能を見てみましょう(Linuxでの最新のGoogle Chrome):







画像







レイヤー構成ステップがどれだけ拡大したかに注目してください。 要素が1つしかないので、少し非論理的に見えますが、そこに何が長く組み立てられるのでしょうか。 しかし、2Dキャンバスを使用する場合、この動作は珍しくなく、それと関係があることは非常に問題があります。 これがWebGLを使用する傾向がある理由の1つであり、そのような質問はありません。







2DキャンバスとWebGLを選択できる場合は、2番目を選択します。 これにより、同じタスクで初期パフォーマンスボーナスが得られます。

通常、WebGLには何が関連付けられていますか? シェーダー付き。 そして、シェーダーのデバッグは、シェーダーで作業する人にとっては頭痛の種です。 そして、ここの開発者ツールは実質的に無力です。 通常、シェーダーの計算が多すぎる場合、ほとんどの時間は「単純」であることが下の要約でわかります。これは実際、ブラウザーに関係なくシェーダーを実行するため、有用な詳細を取得することはできません。







シェーダーでは他の機能よりもどの機能を優先するかについて、さまざまな推奨事項があります。これらの機能はおそらくより最適化されているためです。 または、そのブロッキング操作を避ける必要があります。 これはすべて真実ですが、私の観察によると、ほとんどの場合、サイトを遅くしすぎるシェーダーは非常に大きなシェーダーです。 1つの場所で100のGLSL行を記述した場合、これはほとんど動作しないことが保証されています。 また、さまざまなネストされた構造、ループ、そしてすべてがある場合、書き込みはなくなります。 以下を除き、ここで推奨事項を作成することは困難です。







作業中にすべてが当初のように複雑であり、多くのコードが存在し、速度が低下することに気付いた場合は、できるだけ早くデザイナーと顧客と話し合い、何が変更できるかを考えた方がよいでしょう。

事前に準備されたビデオは、ある種の混乱したものをリアルタイムでレンダリングしようとするよりもはるかにうまく機能するという結論に至ることがよくあります。 これを覚えて。 はい、誰もが自分を見せたいと思っています。「しかし、私はそのようにできます」と自慢したいのですが、エンドユーザーを忘れないでください。







この考えに関連して、前オリンピアードが特に影響を受けやすい「病気」を思い出します。 何らかの理由で、canvasで作業するときに強く現れます。 そのため、そのような人々のコードは常に慎重にコピーする必要があります。 彼らは「正しい」数学アルゴリズム、複雑な物理式を使用して、完全に役に立たない場合でも、要素のすべての動きを非常に正確に計算しようとします。 これにより、プロセッサの負荷が増加し、条件付き10ミリ秒では何もカウントする時間がありません。 実際には、おおよその式と物理学の学校の知識を習得できます。 物事を複雑にする必要はありません。弾道ミサイル用のソフトウェアではなく、ウェブサイトを作成します。







単純なアルゴリズムを使用します。

RayMarchingと呼ばれる別のトリックがあります。 一部の人々は、それによるさまざまな効果の作成を挑戦、心のウォームアップのようなものと考え、時には結果は非常に印象的です。 たとえば、ここでは水中の世界全体が生成されます(これをリアルタイムで計算すると、電話/ラップトップがハングアップする可能性があるため、ビデオを挿入しました)。









シェーダー自体はここにあります







実際には、これらすべてを機能させるには信じられないほどのリソースが必要です。 フルスクリーンモードでは、フレームごとに400〜800ミリ秒があります(一般に、この例では最大1500ミリ秒になります)。







画像







そのため、戦闘サイトでこのようなことをしようと考えている場合は、頭にキーボードを付け、お茶を飲み、効果を実装するための代替オプションについて考えてください。







RayMarchingを使用しないでください。これはパフォーマンスを低下させる確実な方法です。


実用例



パフォーマンスの記事には十分な例がないことがよくありますが、一言で言うのは難しい場合があります。 いくつか考えてみてください。 最初のCSS回転トンネルの例を覚えていますか? ブラウザは多くのことを行いました:







画像







少しスピードアップしたいです。 どこから始めますか? 紫色のブロックが表示されます。つまり、ブラウザーは常にレイアウトを再構築しています。 スクリプトはありませんが、何かが変化するCSSアニメーションがあります。 コードを見てみましょう。







 @keyframes rotate { from { transform: rotate(0); } to { transform: rotate(360deg); } } @keyframes move-block { from { transform: translateX(0); background: @color1; } to { transform: translateX(-@block-size * 6); background: @color2; } }
      
      





変換は私たちを怖がらせませんが、要素の背景に変化が見られます。 これがレイアウトの再構築を引き起こす可能性があることを思い出し、この状況で何ができるかを考えます...







背景の変更はすべてのコストで削除する必要があるため、アニメーションの一般的な考え方に基づいて、放射状のグラデーションを上部に配置すると、ほぼ同じボリューム効果が得られると判断します。 勾配はパフォーマンスに悪い影響を与えると言う人もいますが、変更するつもりはありません。 私たちが絶えず悪影響を与える要素の山全体を得るよりも、それが悪影響を与えたらそれを改善しましょう。 結果は次のとおりです。









ブラウザの機能を見てみましょう。







画像







わあ...たくさんのアクションの代わりに、GPUへのまれな呼び出しがありますが、アニメーション自体は著しくスムーズに動作し始めました。







別の例



ノイズジェネレーターのブラウザーの様子を思い出してください。







画像







問題は間違いなくスクリプトにあります。 「レンダリング」ブロックが最大であることがわかります。 これは、画像をレンダリングするための主な機能です。 彼女を見てみましょう:







 function render() { let imageData = CTX.createImageData(CTX.canvas.width, CTX.canvas.height); for (let i = 0; i < imageData.data.length; i += 4) { const color = getRandom(); imageData.data[i] = color; imageData.data[i + 1] = color; imageData.data[i + 2] = color; imageData.data[i + 3] = 255; } CTX.putImageData(imageData, 0, 0); requestAnimationFrame(render); }
      
      





個々のピクセルで進行中の作業は間違いなくあります。 これはあまり健康的ではありません。 可能であれば、2DキャンバスではなくWebGLを使用した方が良いと言いました。このタスクは、シェーダーを使用して並列化したいだけです。 やってみましょう:









結果はどうなりますか? 自分で見てください:







画像







1フレームの時間はほぼ16msに減少しました。 もちろん、これは理想的ではありませんが、それでも80msよりも優れています。 複雑で美しいアニメーションでは、このようなパフォーマンスの向上は非常に顕著です。 この機会を利用して、初心者がプログラミングでのシェーダー導入 と例続きに慣れることをお勧めします。







おわりに



この記事では、アニメーションパフォーマンスの最適化、このコンテキストでのChromeの開発者ツールの使用方法、最初に探すべきことを把握しました。 この情報が、このようなタスクに初めて直面し、どこから始めればよいかわからない開発者に役立つことを願っています。








All Articles