Web Animations API と高性能アニメーション:GPU 加速と composite 最適化
性能优化(更新: 2026年6月6日)
ブラウザレンダリングパイプラインとアニメーション性能
1フレームのレンダリングは Style → Layout → Paint → Composite の順で処理されます。アニメーション性能の鍵は可能な限り多くの段階をスキップすることです:
| プロパティ種別 | 発生段階 | 性能 |
|---|---|---|
width/height/margin |
Layout → Paint → Composite | 低 |
color/background |
Paint → Composite | 中 |
transform/opacity |
Composite のみ | 高 |
transformとopacityだけが Layout と Paint をスキップし、GPU の Composite レイヤーで直接合成できます。
GPU 加速:composite レイヤーの仕組み
合成レイヤーへの昇格条件
以下の条件でブラウザは要素を独立した合成レイヤーに昇格させます:
transform: translateZ(0)またはwill-change: transform<video>、<canvas>、WebGL コンテンツtransformまたはopacityに対する CSSanimation/transitionposition: fixed(一部ブラウザ)
合成レイヤーのコスト
各合成レイヤー = 独立した GPU テクスチャ = VRAM 消費
レイヤー過多 → VRAM 圧迫 → 逆に性能低下
推奨:合成レイヤー数は 30 以下に抑える。モバイルではさらに少なく。
will-change:正しい使い方とよくある誤解
基本的な使い方
.animating-element {
will-change: transform, opacity;
}
ベストプラクティス
/* 悪い例:グローバルに宣言、リソースの無駄遣い */
.card {
will-change: transform;
}
/* 良い例:インタラクション直前にのみ宣言 */
.card:hover {
will-change: transform;
}
.card.is-dragging {
will-change: transform;
}
// JS で will-change を動的制御
element.addEventListener('mouseenter', () => {
element.style.willChange = 'transform';
});
element.addEventListener('transitionend', () => {
element.style.willChange = 'auto';
});
原則:will-change は「最適化スイッチ」ではなく「予告」です。アニメーション直前にのみ設定し、終了後に削除します。
Web Animations API:element.animate()
基本構文
const animation = element.animate(
[
{ transform: 'translateX(0px)', opacity: 1 },
{ transform: 'translateX(300px)', opacity: 0.5 }
],
{
duration: 600,
easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
fill: 'forwards',
iterations: 1
}
);
CSS Animations との比較
| 機能 | CSS Animations | Web Animations API |
|---|---|---|
| 宣言方法 | CSS @keyframes |
JS element.animate() |
| 動的制御 | 困難(クラス操作が必要) | 簡単(play()/pause()/reverse()) |
| 動的キーフレーム | 非対応 | 実行時生成に対応 |
| Promise 統合 | なし | animation.finished/animation.ready |
| 性能 | 同じ | 同じ(同じエンジン) |
| ブラウザ最適化 | 事前解析可能 | 実行時最適化 |
アニメーション制御 API
const anim = element.animate(keyframes, options);
anim.pause();
anim.play();
anim.reverse();
anim.finish();
anim.cancel();
anim.playbackRate = 2.0;
anim.currentTime = 300;
anim.finished.then(() => {
console.log('アニメーション完了');
});
requestAnimationFrame とアニメーションスケジューリング
なぜ setTimeout/setInterval ではダメなのか
// 悪い例:画面リフレッシュと非同期、フレームドロップの可能性
setInterval(() => {
element.style.transform = `translateX(${x}px)`;
}, 16);
// 良い例:ブラウザのリフレッシュレートと同期
function animate(timestamp) {
const progress = (timestamp - startTime) / duration;
element.style.transform = `translateX(${targetX * progress}px)`;
if (progress < 1) requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
rAF と WAAPI の組み合わせ
// 複雑なシナリオ:WAAPI で単純アニメーション、rAF でフレーム単位の計算
const scrollAnim = element.animate(
[{ transform: 'translateY(0)' }, { transform: 'translateY(-100px)' }],
{ duration: 300, fill: 'forwards' }
);
// スクロール駆動アニメーションには rAF が必要
let ticking = false;
window.addEventListener('scroll', () => {
if (!ticking) {
requestAnimationFrame(() => {
element.style.transform = `translateY(${window.scrollY * 0.5}px)`;
ticking = false;
});
ticking = true;
}
});
Composite レイヤー最適化の実践
再描画領域の削減
/* 悪い例:コンテナ全体が再描画 */
.container {
animation: pulse 1s infinite;
}
@keyframes pulse {
0%, 100% { background: #fff; }
50% { background: #f0f0f0; }
}
/* 良い例:アニメーションレイヤーのみ再描画 */
.container {
position: relative;
}
.container::after {
content: '';
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0);
animation: pulse 1s infinite;
will-change: opacity;
}
@keyframes pulse {
0%, 100% { opacity: 0; }
50% { opacity: 0.05; }
}
レイヤー爆発の回避
/* 悪い例:全リスト項目が合成レイヤーを作成 */
.list-item {
will-change: transform;
}
/* 良い例:アニメーション中の要素のみ昇格 */
.list-item.is-animating {
will-change: transform;
}
contain プロパティによる隔離
.card {
contain: layout style paint;
}
contain はブラウザに「この要素のスタイル/レイアウト/描画は外部に影響しない」ことを伝え、安全な最適化を可能にします。
性能計測
Chrome DevTools Layers パネル
- DevTools → More tools → Layers を開く
- 合成レイヤー数と VRAM 使用量を確認
- 不要なレイヤー昇格を特定
Performance パネル分析
フレームタイムライン:
- 緑 = Paint
- 紫 = Layout
- 透明 = Composite ← 理想的な状態
FPS 検出
let lastTime = performance.now();
let frames = 0;
function measureFPS() {
frames++;
const now = performance.now();
if (now - lastTime >= 1000) {
console.log(`FPS: ${frames}`);
frames = 0;
lastTime = now;
}
requestAnimationFrame(measureFPS);
}
requestAnimationFrame(measureFPS);
最適化チェックリスト
- アニメーションは
transformとopacityのみ使用 -
width/height/top/leftのアニメーションを避ける -
will-changeはアニメーション前に設定、終了後に削除 - 合成レイヤー数 < 30
-
contain: layout style paintで再描画範囲を隔離 - 複雑なアニメーションには Web Animations API で詳細制御
- スクロール駆動アニメーションは
requestAnimationFrameでスロットル
まとめ
高性能アニメーションの核心はGPU に得意なことをさせることです。transform と opacity のみアニメーションし、will-change でブラウザに予告し、合成レイヤー数を制御して VRAM の無駄を防ぎます。Web Animations API は CSS アニメーションと同じ性能基盤を提供しながら、JS レベルのきめ細かな制御をもたらします。
CSSアニメーションツール でアニメーション効果をプレビュー、グラデーションジェネレーター で高性能な背景アニメーションを作成、画像変換 で画像の transform 操作を行えます。
ブラウザローカルツールを無料で試す →
#Web Animations API#动画性能#requestAnimationFrame#GPU加速#composite