Reactでアニメーションを実装するとき、requestAnimationFrame
を活用することで、スムーズで効率的なアニメーションが実現できます。本記事では、requestAnimationFrame
を使った再帰的アニメーション処理を実装し、その動作原理や効果的な制御方法について解説します。
1. requestAnimationFrame
の概要
requestAnimationFrame
は、ブラウザが描画するフレームに合わせて関数を実行するためのAPIです。これにより、アニメーションがスムーズに動き、無駄なリソースを消費することなく効率的に動作します。
主な特徴:
- 60FPS(1秒間に最大60回)で実行される。
- ブラウザのリフレッシュレートに合わせて描画されるため、カクつきが少なく、スムーズなアニメーションが可能。
- バックグラウンドタブでは自動的に停止し、リソースを節約。
2. requestAnimationFrame
を使ったアニメーションの実装
Reactの useEffect
を使って、requestAnimationFrame
を実装し、アニメーションをループさせる基本的な方法を見ていきましょう。以下は、物体の位置を更新しながらスムーズにアニメーションを実行するサンプルコードです。
import React, { useState, useEffect } from 'react'; const MovingObject = () => { const [position, setPosition] = useState({ x: 0, y: 0 }); const [velocity, setVelocity] = useState({ x: 1, y: 1 }); const [inertia, setInertia] = useState(true); useEffect(() => { if (!inertia) return; const friction = 0.95; let currentVelocity = { ...velocity }; let animationFrameId: number; const animate = () => { setPosition((prev) => ({ x: prev.x + currentVelocity.x, y: prev.y + currentVelocity.y, })); currentVelocity = { x: currentVelocity.x * friction, y: currentVelocity.y * friction, }; if ( Math.abs(currentVelocity.x) < 0.1 && Math.abs(currentVelocity.y) < 0.1 ) { setInertia(false); return; } animationFrameId = requestAnimationFrame(animate); }; animate(); return () => cancelAnimationFrame(animationFrameId); }, [inertia, velocity]); return ( <div style={{ position: 'absolute', left: position.x, top: position.y }}> {/* ここに動かすオブジェクトのコンテンツを追加 */} <div>動いています!</div> </div> ); }; export default MovingObject;
3. requestAnimationFrame
と再帰的呼び出し
上記のコードでは、requestAnimationFrame(animate)
を使って、animate
**** 関数が次のフレームで呼び出される ようにしています。これを繰り返すことで、アニメーションが継続的に実行されます。
const animate = () => { setPosition((prev) => ({ x: prev.x + currentVelocity.x, y: prev.y + currentVelocity.y, })); // 速度を減速 currentVelocity = { x: currentVelocity.x * friction, y: currentVelocity.y * friction, }; // 速度がほぼゼロになったらアニメーションを停止 if (Math.abs(currentVelocity.x) < 0.1 && Math.abs(currentVelocity.y) < 0.1) { setInertia(false); return; } // 次のフレームをリクエスト animationFrameId = requestAnimationFrame(animate); };
このように、requestAnimationFrame
を再帰的に呼ぶことで、1フレームごとにオブジェクトの位置や状態を更新し、アニメーションを滑らかに動かすことができます。
4. cancelAnimationFrame
でアニメーションを制御
アニメーションの停止には、requestAnimationFrame
で得られた ID を使って cancelAnimationFrame
を呼びます。このIDを利用して、アニメーションを停止させることができます。
return () => cancelAnimationFrame(animationFrameId);
useEffect
内で返すクリーンアップ関数により、コンポーネントがアンマウントされる際やアニメーションが終了する際に、アニメーションを止めることができます。
5. 再帰呼び出しとパフォーマンスの最適化
requestAnimationFrame
を使って再帰的に関数を呼び出すと、アニメーションは高頻度で更新されますが、ブラウザのリフレッシュレートに合わせて制御されるため、過剰に処理が呼ばれることはありません。これにより、アニメーションがスムーズに動作し、パフォーマンスに悪影響を与えません。
さらに、アニメーションが必要ないときは、inertia
のような状態を用いてアニメーションを停止することができます。
if (Math.abs(currentVelocity.x) < 0.1 && Math.abs(currentVelocity.y) < 0.1) { setInertia(false); // 速度が低くなったらアニメーションを停止 return; }
6. 終わりに
requestAnimationFrame
を使用すると、効率的でスムーズなアニメーション を実現できます。ブラウザの描画タイミングに合わせてアニメーションを実行するため、カクつきが少なく、リソースの無駄使いを防ぐことができます。\
アニメーションのフレームレートを調整する方法や、cancelAnimationFrame
でアニメーションを停止する方法も紹介しました。
これらをうまく組み合わせることで、Reactでのアニメーション処理がより効果的に、そしてパフォーマンスを維持しながら実装できるようになります。