2
0

【個人開発】Next.jsでのWebサイト制作 #2 共通機能(React hooks × css animateによるアニメーション機能)の実装

Last updated at Posted at 2024-07-24

Next.jsでのWebサイト制作 #2では、共通機能(React hooks × css animateによるアニメーション機能)の実装を紹介をしたいと思います。

#1はこちらから読めます。
↓↓↓

アニメーションの共通化

今回制作したWebサイトでは、以下2つのアニメーションをあらゆる部分で共通使用しています。

  • 画面更新後の秒数をトリガーに発火するアニメーション
  • スクロールをトリガーに発火するアニメーション

今回はこれらのアニメーションの実装方法を紹介します。

また、前回のレイアウト同様、上記アニメーションは共通機能になるため、app/componentsの直下にAnimation.tsxを作成し、実装しました。

image.png

画面更新後の秒数をトリガーに発火するアニメーション

まずは、画面更新後の秒数をトリガーに発火するアニメーション機能の紹介です。

↓イメージ
タイトルなし.gif

上記イメージの通り、トップ画面を読み込んでから数秒後に、メイン画像に被せるようにメッセージが左からスライドインして出現します。

実装方法

// time:何秒後に出現させるか,  direction:要素の移動方向
export const TimeFadeIn = ({ children, time, direction }: any) => {
  // 要素の出現を管理
  const [inView, setInView] = useState(false);

  useEffect(() => {
    // コンポーネントがマウントされた後、time秒後に inViewをtrue に設定
    const timer = setTimeout(() => {
      setInView(true);
    }, time);

    // コンポーネントがアンマウントされた際にタイマーをクリア
    return () => clearTimeout(timer);
  }, []);

  return (
    // inViewがtrueになったらopacity-100となり表示される
    <div
      className={`${inView ? "opacity-100" : `opacity-0 ${direction === "right" ? "translate-x-[50%]" : "translate-x-[-50%]"}`} 
      duration-[1s]`}
    >
      {children}
    </div>
  );
};

上記の通り、useState, useEffect, setTimeoutを組み合わせることで、
コンポーネントがマウントされてから、引数で与えられた秒数がたった際に、stateがtrueになり、それに応じてJSX内のTailwindが動的に変化するよう実装しました。

Tailwindではopacityとtranslateを動的に変化させています。

引数のdirectionは要素の移動方向を判断するためのものであり、rightの場合は"translate-x-[50%]"として右に移動 、それ以外の場合は "translate-x-[-50%]"として左に移動するようにしています。

上記により、「inView」がtrueになったら、「1秒かけて、要素を右移動しながら出現させる。」という動きを実現しています。

スクロールをトリガーに発火するアニメーション

続いて、スクロールをトリガーに発火するアニメーション機能の紹介です。

↓イメージ
タイトルなし.gif

GIFだと分かりづらいかと思いますので、ぜひ実際にHPにアクセスして試してみてください。
https://ty-official-hp-14.vercel.app/

上記の通り、画面をスクロールした際に、さまざまなアニメーションとともに要素を出現させる機能を実装しました。

実装方法

// animation:アニメーションの種類,  rootMargin:どのくらいスクロールした時に発火させるか
export const Animation = ({
  children,
  animation,
  rootMargin,
}: {
  children: any;
  animation: string;
  rootMargin: string;
}) => {
  // ref要素(以下のdivタグ)がrootMarginに来た時点で、inViewがtrueになる
  const { ref, inView } = useInView({
    // ここに位置を設定
    rootMargin: rootMargin,
    // 最初の一度だけ実行
    triggerOnce: true,
  });

  return (
    <div
      ref={ref}
      className={`${
        // inViewがtrueになったら、引数で受け取ったアニメーションを使用して要素を表示
        inView ? "animate__animated animate__" + animation : "opacity-0"
      } `}
    >
      {children}
    </div>
  );
};

上記の通り、css animateとreact-intersection-observerのuseInViewを組み合わせることで、スクロール状況を監視&検知し、アニメーションとともに要素を出現させるという動きを実現しています。

こちらについては、以下の記事で実装方法を詳しく説明しておりますので、ここでの説明は割愛させていただきます。

記事内のサンプルコードにおける、rootMargin:に設定する数値と、対象要素のclassNameに割り当てるanimateの種類を引数で受け取るようにして、汎用性を高めたものが上記のコードになります。

最後に

今回は、プロジェクト全体で使用している共通機能について、紹介しました。

次回はトップページの内容について、紹介したいと思います。
トップページは、three.jsで3Dモデルを表示したりと、紹介したい内容がたくさんありますので、ぜひ次回もお読みいただければ幸いです!

ありがとうございました。

p.s.
未熟エンジニアなもので、間違った技術の使い方等、たくさんしていると思います。
そんな時はコメントで教えていただけるととても嬉しいです。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0