logo
回到上一頁

在 React 中加入基本「滾動顯示」過場效果

Title 1
Title 2
Title 3
Title 4

為網站加上過場效果,會大大地提升網站體驗,用戶也願意花更多時間在網站上停留。互動式的效果通常會透過以下鼠標事件來觸發:

  • 點擊
  • 懸浮 (Hover)
  • 移動
  • 滾動

在這我想和大家分享一段可重覆使用的程式碼,讓每個 React 元件都能很簡易地加上「滾動顯示」的過場效果。過去我們需要透過偵測網站大小和 Body dom 裡面的不同屬性 (如 scrollHeight),來計算目前用戶滾動到的位置,需要顯示哪些對應的元件。現在我們有一個非常方便的 IntersectionObserver API 可以處理這些和「可視範圍」(viewport) 有關的計算。

讓我們直接來看這段程式碼

useScrollToReveal.ts

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

function useScrollToReveal(): {
  domRef: React.MutableRefObject<any>;
  fadeInStyle: React.CSSProperties;
} {
  const domRef = useRef<any>(null);

  const [isVisible, setVisible] = useState(false);

  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        if (!domRef || !domRef.current) return;

        // In your case there's only one element to observe:
        if (entries[0].isIntersecting) {
          setTimeout(() => setVisible(true), 450);
          // No need to keep observing:
          observer.unobserve(domRef.current);
        }
      },
      { rootMargin: '48px' }
    );

    if (domRef && domRef.current) {
      observer.observe(domRef && domRef.current);
    }

    return () => {
      if (domRef && domRef.current) {
        observer.unobserve(domRef.current);
      }
    };
  }, []);

  return {
    domRef,
    fadeInStyle: { transition: 'all .8s ease-in', opacity: isVisible ? 1 : 0 },
  };
}

export default useScrollToReveal;

Example.tsx

import useScrollToReveal from '@hooks/useScrollToReveal';

function RevealBlock({ title }) {
  const { domRef, fadeInStyle } = useScrollToReveal();

  return (
    <div
      {/* Assign the domRef from useScrollToReveal hook, allow the hook know which component to observe */}
      ref={domRef}
      style={{
        ...fadeInStyle,
        height: '300px',
        fontSize: '1.25rem',
        backgroundColor: '#FCD9B8',
        color: '#222',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        marginBottom: '72px',
      }}
    >
      {title}
    </div>
  );
}

function ScrollToRevealExample() {
  return (
    <>
      <RevealBlock title="Title 1" />
      <RevealBlock title="Title 2" />
      <RevealBlock title="Title 3" />
      <RevealBlock title="Title 4" />
    </>
  );
}

export default ScrollToRevealExample;

2024 ❤️ MH (Frank) Tsai