はじめに
Next.jsでサイト制作中に、ページが再レンダリングするとき、アニメーションがリセットされるものとされないものの2種類あることに気づきました。本記事では、以下について詳しく解説します。
- CSS アニメーションがリセットされない理由
- JavaScript アニメーションがリセットされる理由
- 解決策としてのアニメーションのコンポーネント化
最後に簡単なコード例を通して、実際の実装方法をお伝えします。
アニメーションがリセットされるものとされないものの違い
1. CSS アニメーションはリセットされない
CSS のアニメーションは、ブラウザのレンダリングエンジンで再生されています。そのため、React や Next.js のコンポーネントの再レンダリングやページ遷移の影響を受けません。
2. JavaScript アニメーションはリセットされる
JavaScript アニメーションは、レンダリングの影響を直接受けます。レンダリング時に状態がリセットされ、アニメーションが最初からやり直しになることがあります。
再レンダリングで React が古い DOM 要素を削除し、新しい要素を作成した場合、古い要素に紐づいていたJSアニメーションの状態はすべて失われます。
またReact の useEffect や useState は、コンポーネントが再レンダリングされると再実行されるため、JSアニメーションの進行状況がリセットされます。
例:
import { useState, useEffect } from "react";
import JSAnimatedBox from "./JSAnimatedBox";
import CSSAnimatedBox from "./CSSAnimatedBox";
const Page = () => {
const [scrollPosition, setScrollPosition] = useState(0);
useEffect(() => {
const handleScroll = () => {
setScrollPosition(window.scrollY); // スクロール位置を状態で管理
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
return (
<div>
<h1>スクロールアニメーション</h1>
<p>現在のスクロール位置: {scrollPosition}</p>
{/* CSS アニメーション部分 */}
<CSSAnimatedBox />
{/* JavaScript アニメーション部分 */}
<JSAnimatedBox />
</div>
);
};
export default ScrollAnimation;
この場合、コンポーネントが再レンダリングされるとCSSAnimatedBoxコンポーネントは最後までアニメーションが実行され、JSAnimatedBoxのアニメーションは初期化されます。
解決策: アニメーション部分のコンポーネント化
JavaScript アニメーションがリセットされるのを防ぐために、ページ全体がレンダリングされてる要因のコードをコンポーネント化して影響範囲をコンポーネント内に留めておく必要があります
修正版: コンポーネント化されたアニメーション
import ScrollWatcher from "./ScrollWatcher";
import JSAnimatedBox from "./JSAnimatedBox";
import CSSAnimatedBox from "./CSSAnimatedBox";
const ScrollAnimation = () => {
return (
<div>
<h1>スクロールアニメーション</h1>
{/* スクロール監視部分 */}
<ScrollWatcher />
{/* CSS アニメーション部分 */}
<CSSAnimatedBox />
{/* JavaScript アニメーション部分 */}
<JSAnimatedBox />
</div>
);
};
export default ScrollAnimation;
結論
- CSS アニメーションはブラウザのレンダリングエンジンが制御しているため、リセットされることはありません。
- JavaScript アニメーションはレンダリングに依存するため、再レンダリング時にリセットされます。
- レンダリング影響範囲内にjsアニメーション置かないように適宜コンポーネント化する