著者について
Kay Huang は Ctrip のシニア ビジュアル デザイナーで、フロントエンドのスタイルとアニメーションに重点を置いています。
1. 背景
Ctrip の鉄道チケット マーケティング ページでは、長年にわたって CSS を使用してアニメーションを作成してきました。これにより、アニメーションがページに与える豊かな視覚体験が大幅に向上しました。ただし、開発プロセス中に、頭のアニメーションが動かなくなる、ページを開く時間が長い、ページを開いた後の一部のデータの読み込みに時間がかかるなど、パフォーマンス関連の問題やユーザーからのフィードバックも発生しました。これらの問題を解決するために、パフォーマンス検出ツールを使用して問題を特定し、ソース コード、ドキュメント、その他のリソースを参照して問題を解決し、この記事を作成しました。
2. レンダリングの最適化
アニメーションのパフォーマンスを最適化するには、まずブラウザーが要素をレンダリングする方法を理解する必要があります。ブラウザーのレンダリング プロセスには次の 4 つのステップがあります。
a. 要素のスタイルを計算します (おそらくスクリプトによって再計算されます)。
b. 各要素の幾何学的形状と位置 (レイアウト) を生成します。
c. レイヤーの各ピクセルを描画します (描画を初期化し、描画を実行します)。
d. レイヤを画面に描画します (レンダー レイヤをマージします)。
CSS3 アニメーションの場合、各フレームは上記のプロセスを経る必要があります。レンダリング レイヤーを結合する最後のステップ (Photoshop のレイヤーと比較できます) に関しては、ブラウザーは特定の機会に独立したレンダリング レイヤーを作成します。各レンダリング レイヤーは、互いに影響を与えることなく GPU によって独立して描画されます。最後に、ブラウザーはレンダリングを結合します。レイヤー。これは低コストの操作です。
理論的には、FPS が高いほど、アニメーションはよりスムーズになります。現在、ほとんどのデバイスの画面リフレッシュ レートは 60 回/秒であるため、一般的に言えば、FPS が 60 フレーム/秒のときにアニメーション効果が最適になります。フレームあたりの所要時間 (フレーム バジェット) は 16.67 ミリ秒です。
3. 解決策
上で述べたように、ブラウザにはいくつかの仕上げ作業があるため、すべての作業は 10 ミリ秒以内に完了する必要があります。アニメーションがレイアウトをトリガーする場合、それは再描画の 2 番目のステップに相当します。再描画されると、ブラウザーのレンダリング時間は確実に 16 ミリ秒を超え、ページがスタックします。モバイル端末の場合は、遅くなりますので、最適化したい場合は、最初のステップから 4 番目のステップに直接ジャンプする必要があります。
以下にアニメーションのパフォーマンスを最適化する 7 つの方法を書きました。その中には、最初のステップから 4 番目のステップに直接ジャンプするものや、その他の日常的な最適化に関する考慮事項もいくつかあります。
3.1 GPU アクセラレーションをオンにする
Transform プロパティを使用すると、要素に 2D または 3D 変換を適用して、要素の選択、拡大縮小、移動、傾斜を行うことができます。
日常生活では、left/top と translation を使用して要素の移動を実現できますが、実際には、transform 属性はそれ自体とその周囲の要素のレイアウトを変更せず、要素全体に影響を与えます。
まず、top/left プロパティを使用してアニメーションを作成し、効果を確認しましょう。
<style>
.heart{animation: heartbeat 4s infinite;width:50px;height:50px;background:red;position:absolute;left:30px;top:30px;}
@keyframes heartbeat{
0%{top:30px;left:30px;}
25%{top:30px;left:230px;}
50%{top:230px;left:230px;}
75%{top:230px;left:30px;}
}
</style>
Chrome ブラウザの DevTools を使用して表示すると、赤い四角がすべてレイアウトの再描画であることがわかります。
ブラウザが多くの計算を実行し、アニメーションがフリーズするため、画像内に非常に多くの赤いボックスとフレーム番号が表示されます。
すべてのフレームが変化するため、ブラウザは新しいビットマップをレイアウト、描画し、GPU メモリに転送します。要素の位置が変更されるだけですが、子要素の位置も同期して変更される可能性が高いため、ブラウザは次のことを行う必要があります。 recalculate レイアウトが計算された後、メインスレッドは要素のビットマップを再生成します。
次に、アニメーションをトランスフォームに置き換えます。
<style>
.heart{animation: heartbeat 1s;width:50px;height:50px;background:red;}
@keyframes heartbeat{
0%{transform: translate(30px,30px);}
25%{transform: translate(30px,230px);}
50%{transform: translate(230px,230px);}
75%{transform: translate(230px,30px);}
}
</style>
再度分析すると、赤い再描画四角形はなく、フレーム番号が 1 つしかないことがわかります。そのため、アニメーションは確実に滑らかで滑らかなものになることがわかります。
変換属性はそれ自体とその周囲の要素のレイアウトを変更しないため、要素全体に影響を与えます。
ノードの変換を通じて、ノードの位置、回転、サイズなどを変更できます。通常、ノードの位置を変更するには left 属性と top 属性を使用しますが、前述したように、left 属性と top 属性は再レイアウトをトリガーするため、変更のコストが非常に高くなります。より良い代替方法は、再レイアウトをトリガーしない翻訳を使用することです。
3.2 パフォーマンスに影響を与える CSS プロパティの使用を避ける
これらのプロパティは、特にアニメーションで使用する場合、複雑な計算とレンダリングを必要とするため、パフォーマンスに影響を与える可能性があります。これらのプロパティにより、ブラウザーのリフローや再描画が発生し、ページのパフォーマンスや滑らかさに影響を与える可能性があります。
これらのプロパティを使用する必要がある場合は、可能な限り使用を最小限に抑えてください。たとえば、ブラウザのハードウェア アクセラレーションをより有効に活用できるため、CSS3 変換プロパティを使用してボックス シャドウ効果やボーダー半径効果を実現してみることができます。
たとえば、box-shadow プロパティを使用した例を次に示します。
.box {
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
}
box-shadow 属性は要素の周囲に影の効果を追加しますが、ブラウザは複雑な計算とレンダリングを行う必要があるため、パフォーマンスに影響します。パフォーマンスを最適化するために、CSS3 変換プロパティを使用して同じ効果を実現できます。
.box { transform: translateZ(0); box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);}
この変換属性によりハードウェア アクセラレーションが有効になり、パフォーマンスが向上します。同時に、box-shadow プロパティを使用して影効果を追加することもできます。
3.3 複雑なセレクターの使用を避ける
セレクターとアニメーションの間には特定の関係があります。CSS アニメーションでは、セレクターの複雑さが増すほど、スタイルの計算にかかる時間が長くなります。アニメーションで複雑なセレクターを使用すると、ブラウザーがスタイルを計算するのに時間がかかり、アニメーションのパフォーマンスと滑らかさに影響します。
たとえば、複雑なセレクターを使用して要素を選択し、それらにアニメーション効果を追加すると、ブラウザーがスタイルを計算するのに時間がかかり、アニメーションのパフォーマンスと滑らかさに影響します。対照的に、単純なセレクターを使用して要素を選択し、アニメーション化すると、ブラウザーはスタイルをより速く計算でき、アニメーションのパフォーマンスと滑らかさが向上します。
複数の項目を含むリストがあるとします。これらの項目に簡単なアニメーションを追加して、マウスをその上に置くと項目の背景色が青に変わるようにしたいと考えています。次の CSS コードを使用して、この効果を実現できます。
/* 使用类选择器来选择所有项目 */
.item {
background-color: #fff; /* 初始背景色为白色 */
transition: background-color 0.3s ease; /* 添加背景色渐变动画 */
}
/* 当鼠标悬停在项目上时,将背景色渐变为蓝色 */
.item:hover {
background-color: #007bff; /* 背景色渐变为蓝色 */
}
この例では、クラス セレクターを使用してすべての項目を選択し、それらに初期背景色と背景色のグラデーション アニメーションを追加します。マウスが項目上にあるとき、hover 疑似クラス セレクターを使用して、現在ホバーされている項目を選択し、その背景色を青にグラデーションします。
この例のセレクターは非常にシンプルで、ブラウザーはスタイルをすばやく計算できるため、アニメーションのパフォーマンスと滑らかさが向上します。対照的に、複雑なセレクターを使用して項目を選択し、それらにアニメーション効果を追加すると、ブラウザーがスタイルを計算するのに時間がかかり、アニメーションのパフォーマンスと滑らかさに影響します。
3.4 will-change の使用
will-change 属性を使用して、どの要素をアニメーション化するかをブラウザーに伝え、ブラウザーが事前に最適化できるようにします。
will-change 属性は CSS3 の新しい属性で、どの要素がアニメーション化されるかをブラウザーに伝えることができるため、ブラウザーは事前に最適化してアニメーションのパフォーマンスと滑らかさを向上させることができます。
たとえば、要素をアニメーション化したい場合は、CSS に次のコードを追加できます。
#textbox {
opacity: 1; /* 初始透明度为1 */
transition: opacity 0.3s ease; /* 添加透明度渐变动画 */
will-change: opacity; /* 告知浏览器我们将会修改透明度 */
}
この例では、will-change 属性を使用してテキスト ボックスの透明度を変更することをブラウザーに伝え、ブラウザーが事前にテキスト ボックスを最適化できるようにします。アニメーションが開始されると、ブラウザーは対応するリソースをすでに準備しているため、アニメーションのレンダリングが高速になり、アニメーションのパフォーマンスと滑らかさが向上します。
will-change 属性の使用には注意が必要です。これを使用すると、ブラウザーが追加のメモリとリソースを事前に割り当てて、ページのパフォーマンスに影響を与える可能性があるためです。したがって、will-change 属性は必要な場合にのみ使用する必要があります。
CSS3 will-change は Web 標準プロパティであり、互換性の点で Chrome/FireFox/Opera でサポートされています。
will-change 属性の使用は、CSS アニメーションを最適化するための重要なテクニックの 1 つであり、アニメーションのパフォーマンスと滑らかさを向上させることができます。
3.5 requestAnimationFrame の使用
requestAnimationFrame は、ブラウザーの最適化を最大限に活用するため、アニメーションを実行するために setTimeout または setInterval の代わりに使用されます。
requestAnimationFrame はブラウザーによって提供される API であり、これを使用すると、ブラウザーが次の描画を行う前にアニメーションを実行できます。setTimeout や setInterval と比較して、requestAnimationFrame はブラウザの最適化をより効果的に活用できるため、アニメーションのパフォーマンスと滑らかさが向上します。
たとえば、要素をアニメーション化したい場合は、requestAnimationFrame を使用してアニメーションを実行できます。
function animate() {
// 更新元素的样式
element.style.transform = 'translateX(100px)';
// 使用requestAnimationFrame执行下一帧动画
requestAnimationFrame(animate);
}
// 开始执行动画
requestAnimationFrame(animate);
上記のコードでは、requestAnimationFrame を使用してアニメーションを実行します。アニメーションの各フレームで要素のスタイルを更新し、requestAnimationFrame を使用してアニメーションの次のフレームを実行します。これにより、ブラウザの最適化が最大限に活用され、アニメーションのパフォーマンスと滑らかさが向上します。
requestAnimationFrame はすべてのブラウザでサポートされているわけではないため、使用する場合は互換性を考慮する必要があることに注意してください。通常、ポリフィルを使用して requestAnimationFrame の互換性を実現できます。
3.6 アニメーション内で JavaScript を使用して DOM を操作しないようにする
アニメーション内で JavaScript を使用して DOM を操作すると、パフォーマンスに影響します。主な理由は、DOM 操作はリフローや再描画を引き起こすため、リソースを非常に大量に消費するためです。DOM 上の各操作により、ブラウザーが要素のレイアウトを再計算し、要素を再描画します。これらの操作は大量の CPU リソースとメモリを消費し、アニメーションが停止したり滑らかでなくなったりする原因になります。
アニメーションでは、DOM を頻繁に操作する必要がある場合、パフォーマンスの問題が発生します。たとえば、JavaScript をアニメーションで使用して要素の位置、サイズ、スタイル、その他の属性を変更すると、DOM 操作がトリガーされ、アニメーションの滑らかさに影響します。
DOM を操作するために JavaScript を使用する必要がある場合は、JavaScript の使用をできる限り減らしてください。たとえば、アニメーションの開始前に、操作する必要がある要素を変数にキャッシュしておき、要素を毎回再検索する代わりに、アニメーション中にそれらの変数を直接使用できます。
さらに、JavaScript の代わりに CSS3 アニメーション プロパティを使用して DOM を操作することもできます。たとえば、animation 属性を使用すると、JavaScript を使用して DOM を操作しなくても、複雑なアニメーション効果を実現できますが、JavaScript アニメーションの代わりに CSS アニメーションを使用することが最適な理由については、以下で詳しく説明します。
3.7 CSS アニメーションを使用し、JavaScript アニメーションは使用しないようにする
前者の方がブラウザの最適化をより効果的に活用できるためです。
ボタンがあり、ユーザーがボタンをクリックしたときに、テキスト ボックスを画面から削除し、削除されたときに単純なアニメーションを追加したいとします。次の JavaScript コードを使用して、この効果を実現できます。
var textbox = document.getElementById('textbox'); // 获取文本框元素
var button = document.getElementById('button'); // 获取按钮元素
button.addEventListener('click', function() {
textbox.style.opacity = 0; // 文本框透明度渐变为0
setTimeout(function() {
textbox.parentNode.removeChild(textbox); // 移除文本框元素
}, 300); // 延迟300毫秒后移除文本框元素
});
この例では、JavaScript を使用して DOM 要素を操作します。これには、テキスト ボックス要素とボタン要素を取得し、ボタンがクリックされるとテキスト ボックスの透明度を徐々に 0 に下げ、300 ミリ秒後にテキスト ボックス要素を削除します。
ただし、このアプローチではブラウザのリフローと再描画が発生し、アニメーションのパフォーマンスと滑らかさに影響します。代わりに、CSS3 トランジション プロパティを使用して、JavaScript を使用して DOM 要素を操作しなくても、単純なアニメーション効果を実装できます。
たとえば、次の CSS コードを使用して、ユーザーがボタンをクリックするとテキスト ボックスが徐々に消える単純なアニメーション効果を実装できます。
#textbox {
opacity: 1; /* 初始透明度为1 */
transition: opacity 0.3s ease; /* 添加透明度渐变动画 */
}
#textbox.hide {
opacity: 0; /* 透明度渐变为0 */
}
この例では、CSS3 トランジション プロパティを使用して、単純な透明度のグラデーション アニメーションを実装します。ユーザーがボタンをクリックすると、JavaScript を使用してテキスト ボックスに非表示クラスを追加します。このクラスは、テキスト ボックスの透明度を徐々に 0 に下げ、テキスト ボックスが徐々に消えるアニメーション効果を実現します。
この例のアニメーション効果は、JavaScript を使用して DOM 要素を操作することなく、DOM 要素に直接適用できるため、アニメーションのパフォーマンスと滑らかさが向上します。
アニメーションで CSS アニメーションを使用すると、ブラウザのハードウェア アクセラレーションをより効果的に活用できるため、アニメーションのパフォーマンスと滑らかさが向上します。比較すると、JavaScript アニメーションでは通常、より多くの計算と操作が必要となり、アニメーションのパフォーマンスと滑らかさに影響します。
もちろん、場合によっては JavaScript アニメーションが必要になる場合もあります。たとえば、ユーザーの操作が必要なアニメーションでは、JavaScript アニメーションを使用すると、アニメーションの動作をより詳細に制御できます。ただし、この場合でも、アニメーションのパフォーマンスと滑らかさを向上させるために、JavaScript 操作の数をできる限り減らす必要があります。
4. 結論
アニメーションはページに豊かな視覚体験を与えます。フレームドロップを避けるために、再レイアウトと再描画をトリガーするプロパティの使用を避けるようにする必要があります。ブラウザがアニメーションを事前に最適化できるように、アニメーションを事前に宣言することをお勧めします。GPU の関与により、現在アニメーションに使用される最適な属性は次のとおりです: * 不透明度 * 変換 * 回転 * スケール、JavaScript の最適化、レイアウトとペイントの削減。ブラウザのレンダリングの仕組みや日々のアニメーション開発について、皆様の理解の一助になれば幸いです。
パフォーマンスの最適化は継続的かつ徹底的な問題です。この記事で紹介した改善策により、ページのパフォーマンスが大幅に最適化され、良好な結果が得られました。今後もこれをベースに改善できる部分を深掘りし、総合的なソリューションを提供していきます。
【おすすめの読書】
「Ctrip Technology」公開アカウント
共有、コミュニケーション、成長