🏎️

React + Intersection Observerで作る超軽量なInViewコンポーネント(Tailwind対応)

に公開

React + Intersection Observerで作る超軽量なInViewコンポーネント(Tailwind対応)

Reactでアニメーションや表示制御によく使う「in-view検出」。今回は、Intersection Observer API を活用した超軽量なInViewコンポーネントを作成し、Tailwind CSSで簡単にスタイリングできる形にまとめます。


なぜ軽量?

  • ライブラリ不使用framer-motionreact-intersection-observerなどに頼らない)
  • IntersectionObserverをラップしただけのシンプルなロジック
  • Tailwindのユーティリティクラスでアニメーション切り替え

そのため、JSバンドルの肥大化を防げて汎用性の高いコンポーネントとして活用できます。


コード全体(コピペOK)

InView.tsx

import { useEffect, useRef, useState } from "react";

type Props = {
  children: React.ReactNode;
  threshold?: number;
  once?: boolean;
  className?: string;
  inViewClass?: string;
};

const InView = ({
  children,
  threshold = 0.1,
  once = true,
  className = "opacity-0 translate-y-8 transition duration-700",
  inViewClass = "opacity-100 translate-y-0",
}: Props) => {
  const ref = useRef<HTMLDivElement>(null);
  const [isIntersecting, setIsIntersecting] = useState(false);

  useEffect(() => {
    if (!ref.current) return;
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setIsIntersecting(true);
          if (once) observer.unobserve(entry.target);
        } else if (!once) {
          setIsIntersecting(false);
        }
      },
      { threshold }
    );
    observer.observe(ref.current);
    return () => observer.disconnect();
  }, [threshold, once]);

  return (
    <div
      ref={ref}
      className={`${className} ${isIntersecting ? inViewClass : ""}`.trim()}
    >
      {children}
    </div>
  );
};

export default InView;

使い方

import InView from "./components/InView";

export default function Home() {
  return (
    <InView>
      <h2 className="text-2xl font-bold">スクロールして表示される要素</h2>
    </InView>
  );
}

用途別パターン(Tailwind class指定)

1. 下からフェードイン(デフォルト)

<InView>
  <div>fade up</div>
</InView>

2. 右からスライドイン

<InView
  className="opacity-0 translate-x-8 transition duration-700"
  inViewClass="opacity-100 translate-x-0"
>
  <div>slide from right</div>
</InView>

3. 拡大表示(ズームイン)

<InView
  className="opacity-0 scale-90 transition duration-700"
  inViewClass="opacity-100 scale-100"
>
  <div>zoom in</div>
</InView>

4. 一度きりでなく毎回実行(once={false}

<InView once={false}>
  <div>in/out で毎回反応</div>
</InView>

まとめ

  • Tailwindと組み合わせれば、柔軟なアニメーション制御が簡単に実現可能
  • 追加ライブラリなしで軽量に実装でき、プロジェクト全体のパフォーマンスにも貢献
  • classNameinViewClassを組み合わせて無限にパターン展開可能

プロジェクト内にコピペして即使えるので、ぜひ試してみてください。


この記事が役に立ったら、いいねやシェアしてくれたら嬉しいです!

Discussion