ScrollRevealを使ってNextJS(React)でスクロールアニメーションを簡単に実装する方法

#はじめに
フロントエンドでReactやNextJSを使用しているときにスクロールアニメーションをコンポーネント化して簡単に使用する方法を見つけたので、共有します。

#スクロールアニメーションを実装する

アプリケーションやWebサイトを作成する時にアニメーションがあったりすると途端に本格的に見えたりしますが、CSSで実装するのは中々面倒だったりします。
今回紹介するのは「scrollreveal」というライブラリとReactを使用して再利用性の高いアニメーションコンポーネントを作成していきたいと思います。

##ScrollReveal
ScrollRevealはJavascriptでスクロールアニメーションが実装できるようになるライブラリで、よくWebサイトで見る「スクロールしたらふわっと出てくる」アニメーション効果を与えてくれます。
[公式ドキュメント](https://scrollrevealjs.org/)

使用するには、`script`タグで読み込むかnpm(yarn)でライブラリをインストールします。

````javascript
//scriptで読み込み
<script src="https://unpkg.com/scrollreveal"></script>
````

````
//npm(yarn)でインストール
npm i -S (yarn add) scrollreveal
````
`typescript`を使用している方は、別途型定義ファイルも必要なので、インストールします。

````
npm i -S -D(yarn add -D) @types/scrollreveal
````

アニメーション効果をつけるには`ScrollReveal().reveal()`メソッドで対象の`class`等を指定すれば、簡単にアニメーション効果をつけることができます。

````javascript
//第二引数で効果時間の設定等が可能。詳しくは公式ドキュメントを参照して下さい
ScrollReveal().reveal('.container', {
 duration: 500,
 reset: true
})
````

##Reactでコンポーネント化
次にReactで要素をラップすればアニメーション効果を付与できるコンポーネントを作成します。

````react:ScrollRevealContainer.tsx
import { FC, useRef, useEffect } from "react";
import scrollReveal from "scrollreveal";

interface ScrollRevealContainerProps {
  move?: string;
}

const ScrollRevealContainer: FC<ScrollRevealContainerProps> = ({
  children,
  move
}) => {
  const sectionRef = useRef<HTMLElement>(null);

  useEffect(() => {
    if (sectionRef.current)
      scrollReveal().reveal(sectionRef.current, {
        reset: true,
        delay: 400,
        opacity: 0,
        origin:
          move === "left"
            ? "left"
            : move === "right"
            ? "right"
            : move === "top"
            ? "top"
            : "bottom",
        distance: "40px"
      });
  }, [sectionRef]);

  return <section ref={sectionRef}>{children}</section>;
};
export default ScrollRevealContainer;

````

このコンポーネントではReactの`useRef`,`useEffect`を用いてアニメーション発動のタイミングを設定しています。
まず`useRef`でこのコンポーネントまでスクロールした時にDOMを参照し、`useEffect`でDOMが参照される度に`scrollreveal`のメソッドを実行するようにします。
また任意で`props`を渡せるようにしており、`left`,`right`,`top`,`bottom`の中から渡した値の方向からコンポーネントがスクロールして表れるようにしています（何も`props`を渡さなければ下部から表れます）。

あとはアニメーションを実装したい要素をこのコンポーネントで囲むだけです。

````react:app.tsx

import ScrollRevealContainer from "./ScrollRevealContainer";

export default function App() {
  return (
    <>
      <div style={{ marginBottom: "400px" }}></div>
      <ScrollRevealContainer move="right">
        <h1>右から登場</h1>
        <h2>Edit to see some magic happen!</h2>
      </ScrollRevealContainer>
      <ScrollRevealContainer move="left">
        <h1>左から登場</h1>
        <h2>Edit to see some magic happen!</h2>
      </ScrollRevealContainer>
      <ScrollRevealContainer move="top">
        <h1>上から登場</h1>
        <h2>Edit to see some magic happen!</h2>
      </ScrollRevealContainer>
      {/* ↓props「move」に何も指定しなければ「origin」は「bottom」になる */}
      <ScrollRevealContainer>
        <h1>下から登場</h1>
        <h2>Edit to see some magic happen!</h2>
      </ScrollRevealContainer>
    </>
  );
}

````

このように要素を`ScrollRevealContainer`コンポーネントで囲むだけで簡単に実装できました！
実際にはこのように動作するので確認してみて下さい↓
[Code Sandbox](https://codesandbox.io/s/distracted-water-uu6t1?file=/src/App.tsx)



##NextJSで使用する際の注意点

Reactでは上記の方法で動作するのですが、同じ方法をNextJSで実装すると`document is not defind`というエラーが表示されます。
これはレンダリング方法の違いによって起こるものであり、**NextJSは開発モード時デフォルトでSSR(Server Side Rendering)でレンダリングされるのですが、サーバーサイドに存在しない`document`を参照しているためにこのようなエラーが発生します。**
解決方法としては`next/dynamic`を使用してコンポーネントを動的インポートし、さらにオプション`ssr:false`を指定して対象のコンポーネントをSSRさせないようにすればOKです。

````react:app.tsx
//動的インポートを使用し、オプションでSSRさせないよう設定
const ScrollRevealContainer = dynamic(
	import('./ScrollRevealContainer'),
	{ssr: false,}
);

````
こうすればエラーが解消されます！

[参考：Next.jsでwindow is not definedを解決する(依存ライブラリ対応)](https://zenn.dev/ninomium/articles/5fed8af69640a6)


#まとめ

React(NextJS)を使用して再利用性の高いアニメーションコンポーネントで作成できました。
今後も新しい情報が入り次第随時更新していきます。