optimize-web-animations について
optimize-web-animationsは、Webアニメーションの性能とクリーンアップを調査・改善するスキルです。実ページを計測し、画面外の処理を停止して表示中に再開し、遷移後のリソース解放を検証します。
主な機能
- 01ブラウザーでのアニメーション計測
- 02画面外のCSSアニメーション停止
- 03Canvas・WebGLループの制御
- 04タイマー・リスナー・リソースの解放
- 05ルートや表示領域をまたぐ動作検証
活用シーン
- 01画面外で動き続けるアニメーションの特定
- 02不要なCanvas・WebGL処理の削減
- 03画面遷移時のリソースリーク監査
- 04モバイルや長いページでの動作確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ mengto/ skills --skill optimize-web-animationsインストール後、Claude Code で/optimize-web-animationsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
agents/openai.yamldemo/expected-output.mdindex.htmlinput.mdPROMPT.mdreferences/browser-profiling.md詳細
Web アニメーションを最適化する
基本ルール
編集する前に、実際のページを計測します。目的は動きをなくすことではなく、画面外の処理を停止し、表示中の動きを正しく再開させ、ルート変更やアンマウント時に長期間保持されるリソースを解放することです。
利用可能な場合は、特に localhost のページで Codex Browser を使用してください。ユーザーから明示的に依頼された場合を除き、Chrome は使用しないでください。
ワークフロー
-
リポジトリの状況を確認します。
AGENTS.mdまたはローカルの指示を読みます。- 早い段階で
git status --shortを実行します。 - ページコンポーネント、アニメーションフック、CSS キーフレーム、
requestAnimationFrame、setInterval、setTimeout、canvas/WebGL/物理演算コンポーネント、メディア要素、GSAP のタイムライン/トゥイーン、既存の表示状態ユーティリティを探します。 - イベントリスナー、オブザーバー、RAF ループ、インターバル、タイマー、外部スクリプト、メディアストリーム、WebGL のテクスチャ/マテリアル/ジオメトリ/レンダラー、アンマウント後に完了する可能性のある非同期処理について、エフェクトのクリーンアップを検索します。
- 作業ツリーに変更がある場合は、最初から必要な変更だけをステージングする計画を立てます。
-
ブラウザーでベースラインを取得します。
- ユーザーが指定した正確なルートを開きます。
- レイアウトが異なる可能性がある場合は、ページ上部、中ほど、フッター/下部のコンテンツ、およびモバイルのビューポートでプロファイルします。
- 計算済みの
animationName、animationPlayState、表示状態を使って CSS アニメーションを数えます。::beforeと::afterも含めます。 - canvas/WebGL 要素は別に調べます。CSS のプロファイリングだけでは、RAF ループが停止したことを証明できません。
- 画面外で実行中のアニメーション名と、それを所有する DOM 要素を記録します。
- メモリ/リークに関する依頼では、要素/canvas/画像/iframe の数、取得可能な場合は公開されている JS ヒープのメトリクス、10~30 秒後のアイドル状態のサンプル、短いルート切り替えのサンプルも記録します。ヒープ API が
nullを返す場合や、Browser のサンドボックスによってモンキーパッチがブロックされる場合は、その旨を伝え、安定して観測できる数値とソース監査に基づいて判断します。 - 負荷テストは範囲を限定します。プロファイリング中に Browser のタブがクラッシュした場合は過負荷の証拠になりますが、最小限のテストで再現しない限り、原因を特定しすぎないでください。
- 再利用可能な Codex Browser evaluator については、
references/browser-profiling.mdを参照してください。
-
動きを制御する最小範囲の所有者にパッチを適用します。
- アプリに既存のページ表示/可視性フックがある場合は、そちらを優先します。
- ない場合は、セクションやアニメーションする子要素に
is-offscreenなどの安定したクラスを切り替えるIntersectionObserverを追加します。 - 対象を絞ったルールで CSS アニメーションを一時停止します。
main > section.is-offscreen .expensive-animation,
.expensive-animation.is-offscreen {
animation-play-state: paused !important;
}
- カードやプレースホルダーが繰り返される場合は、ドキュメント全体ではなく、カードの外枠とアニメーションする子孫要素を監視します。
- マーキー/ティッカーのトラックは、そのセクションが画面外にあるときに一時停止します。
- スケルトンローダーや疑似要素のグリマーには、必要に応じて
::beforeと::afterの一時停止セレクターを含めます。 - Canvas/WebGL/物理演算のループでは、RAF ループを直接制御します。
- Canvas/コンテナーが交差したときに開始します。
- 画面外になったら
requestAnimationFrameをキャンセルします。 - 再び画面内に入ったら再開します。
- クリーンアップ時にオブザーバーを切断し、フレームをキャンセルします。
- ブラウザーでの検証に役立つ場合は、
data-animation-activeなど、視覚に影響しないデバッグマーカーを追加します。
- コンポーネントがすでに
prefers-reduced-motionに対応している場合は、その動作を尊重し、スクロール/アニメーション状態のために React のレンダーループを導入しないでください。 - リーク対策を強化します。
- エフェクトで作成したタイムアウト/インターバルをすべてクリアします。
- アンマウント前とループ再開前に RAF をキャンセルします。
IntersectionObserver、ResizeObserver、MutationObserver、カスタムサブスクリプションを切断します。- 同じハンドラー参照を使って、グローバル/window/document のリスナーを削除します。
- Three/WebGL のテクスチャ、マテリアル、ジオメトリ、レンダラーを破棄し、レンダラーの DOM ノードを削除します。
- DOM ノードやシェーダーのユニフォームなどの可変オブジェクトに対する GSAP のトゥイーン/タイムラインを停止します。
- メディアストリームを停止し、切り離された動画/音声ソースを一時停止します。
- 非同期ローダーを
isDisposedフラグで保護し、アンマウント後に読み込みが完了した場合は、読み込まれたリソースを破棄します。 - React のクリーンアップで、ref が変更される可能性があるという lint の警告が出た場合は、クリーンアップを返す前にエフェクト内で
ref.currentの値を取得します。 - 表示状態から復帰した後に遅延フレームで過大な更新が実行されないよう、物理演算またはシミュレーションのフレームデルタに上限を設けます。
-
ビルドだけでなく、動作も検証します。
- ルートを再読み込みし、同じトップ/中間/フッター/モバイルのプロファイルを再実行します。
- 目標結果:テスト対象のページセクションで
offscreenRunningCount: 0になること。 - 画面内にスクロールしたときに、表示中のアニメーションが動作または再開することを確認します。
- RAF/Canvas ループが画面外では非アクティブ、画面内ではアクティブと報告されることを確認します。あるいは、ソース/ランタイムの状態からキャンセルを証明します。
- リーク監査では、ルート遷移前後とアイドル時のサンプルを比較します。繰り返しナビゲーションした後、DOM/Canvas/画像の数が同じ基準値に戻ることを確認します。ただし、想定される非同期コンテンツの小さな変動は許容します。
- 検索/フィルター/ナビゲーションなど、通常のページ操作を実行し、オブザーバーが動的コンテンツを妨げないことを確認します。
- 新しいタブでコンソールの警告/エラーを確認します。
-
ローカルチェックを実行します。
- リポジトリ標準のチェックを使用します。React/Vite アプリでは、次のようなものが一般的です。
git diff --check
npm run lint
npm run build
- 既知の致命的でない警告は、失敗とは分けて記載します。
- リポジトリ/ユーザーの指示で求められた場合は、変更範囲を絞ってコミットします。
- 無関係な未コミット変更がある場合は、分離したインデックスを使用します。
rm -f /tmp/<task>-index
GIT_INDEX_FILE=/tmp/<task>-index git read-tree HEAD
# Apply only the intended hunks to the temporary index.
GIT_INDEX_FILE=/tmp/<task>-index git diff --cached --check
GIT_INDEX_FILE=/tmp/<task>-index git commit -m "Pause offscreen <page> animations"
git restore --staged <files> 2>/dev/null || true
- 作業ツリーが汚れている場合、すべてのハンクが作業対象に属していない限り、広範なファイルをステージしないでください。
- 根拠を添えて報告します。
- 判明した内容を最初に示します。まだ動作していたもの、リークの懸念があったもの、計測できなかったものを記載します。
- ソース監査上のリスクと、実ブラウザーでの計測結果を分けて記載します。
- 正確にサンプリングしたルート、画面外のアニメーション数、DOM/Canvas 数の安定性、ルート遷移の結果、ローカルチェックを含めます。
- 特に、ヒープカウンターが利用できない場合や、ブラウザーの計測が妨げられた場合は、制約を明確に記載します。
推奨される修正パターン
- 長いセクションでは、セクション単位と要素単位の両方に
is-offscreenを設定します。これにより、画面外にある子要素のアニメーションも実行されないようにできます。 - ルートごとに、
COURSES_PAGE_ANIMATION_VISIBILITY_SELECTORのような共通の表示判定セレクター定数を用意します。 - アニメーションを制御する
IntersectionObserverのしきい値は、0.01前後にします。 - WebGL/canvas のエフェクトでは、RAF ループを直接制御します。CSS の
animation-play-stateでは JavaScript のレンダーループを一時停止できません。 - 一時停止後、またはフレームが遅延した後に再開する物理演算ループには、フレームデルタの上限を設定します。
- GSAP/WebGL エフェクトで使う React の ref については、クリーンアップ対象のノードを取得して保持します。
- アンマウント後に解決する可能性がある画像/動画/テクスチャ/データのローダーには、
isDisposedによるガードを設定します。 - 短時間のアイドル状態とルート切り替えを調べ、DOM ノード、canvas、iframe の蓄積やメディアの解放漏れを検出します。
避けること
- プロファイルの合格を目的に、すべてのアニメーションを削除すること。
- 祖先要素のセレクターが広すぎるために、表示中のヒーローアニメーションまで一時停止すること。
animation-play-stateが疑似要素や JavaScript の RAF ループにも適用されると思い込むこと。- 長いページを、ページ最上部での測定だけで判断すること。
- 利用できないヒープカウンターを、メモリリークがない証拠として扱うこと。
- Browser で上限のないストレステストを実行すること。回数を制限し、クラッシュを記録しつつ、因果関係を誇張しないでください。
- スクリーンショットだけをパフォーマンスの証拠にすること。
- 無関係なローカル変更をコミットに含めること。
コメント
Markdown 対応