본문 바로가기

프론트엔드/Library

[Library] react-intersection-observer 사용법과 예시 (feat. react95)

반응형

[Library] react-intersection-observer 사용법과 예시(feat. react95)

사용 기술 스택: `react-intersection-observer`, `React`, CSSModule`

1. 프로젝트 설정

$ npm install react-intersection-observer --save

 

 

2. 데모 예시

3. 사용 코드

Text, Windows 컴포넌트는 `react-intersection-observer` 라이브러리를 사용하여 스크롤할 때 요소가 화면에 들어오면 애니메이션이나 스타일을 적용할 수 있는 기반을 제공합니다.

 

1) 화면에 글자 보여주기

/* Text.module.css */
.text {
  transition: all 0.3s ease-in-out;
}

.opacity100 {
  opacity: 100;
  transform: translateY(0);
}

.opacity0 {
  opacity: 0;
  transform: translateY(20px);
}
import * as React from "react";
import { useInView } from "react-intersection-observer";
import styles from "./Text.module.css"; // CSS Modules import

export const Text = ({ children }: { children: React.ReactNode }) => {
  const { ref, inView } = useInView({
    triggerOnce: true,
    rootMargin: "-100px 0px",
  });

  const styleClass = inView ? `${styles.opacity100}` : `${styles.opacity0}`;

  return (
    <div ref={ref} className={`${styles.text} ${styleClass}`}>
      <h1>{children}</h1>
    </div>
  );
};

src/app/components/Text.tsx

 

useInView 사용:

  • `ref` : 모니터링할 DOM 요소에 연결될 참조값을 얻습니다. 이 참조값을 div 요소에 연결합니다.
  • `inView` : 요소가 뷰포트 안에 있는지 여부를 나타내는 불리언 값입니다. 요소가 뷰포트 안에 있으면 true, 그렇지 않으면 false입니다.

triggerOnce:

  • true로 설정되어 있어, 요소가 뷰포트에 한 번이라도 들어오면 이후로는 더 이상 감지되지 않습니다.

rootMargin:

  • root란 인터섹션 옵저버가 모니터링하는 영역. 이 영역의 마진이 rootMargin입니다.
  • 뷰포트의 마진을 설정합니다. 요소가 뷰포트에 들어왔을 때 트리거되는 시점을 조정할 수 있습니다. 여기서는 뷰포트 위아래로 100px의 마진을 설정하여, 요소가 뷰포트에서 -100px 지점에 도달했을 때 inView 상태가 true로 변경됩니다.

 

동적 클래스 할당:

  • `inView` 상태에 따라 `styleClass`가 결정됩니다.
  • `inView`가 true이면 styles.opacity100 클래스(불투명)를, false이면 styles.opacity0 클래스(투명) 를 할당합니다.

렌더링:

  • `ref`로 설정된 `div` 요소가 뷰포트에서 감지되도록 설정됩니다.

.opacity0 클래스의 역할:

  • 이 클래스는 요소가 화면에 나타나기 전 초기 상태를 정의하는 데 사용됩니다. 요소는 초기에는 화면에서 보이지 않고, 약간 아래쪽에 위치하게 됩니다. 이후 특정 조건이 만족되면, 다른 클래스가 추가되어 불투명도가 높아지고 원래 위치로 이동하게 됩니다.
  • 이 속성은 요소의 위치를 Y축을 기준으로 20픽셀 아래로 이동시킵니다. `translateY(20px)`는 요소를 현재 위치에서 20픽셀 아래로 이동시키는 것을 의미합니다.

 

/* IntersectionObserver.module.css */
.div {
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
}

.div > h1 {
  font-size: 10vw;
}
import styles from "./IntersectionObserver.module.css";
import { Text } from "./Text";

const TEXTS = ["Don't", "you", "just", "hate", "popups?"];

export default function IntersectionObserver() {
  return (
    <div>
      {TEXTS.map((text) => (
        <div key={text} className={styles.div}>
          <Text>{text}</Text>
        </div>
      ))}
    </div>
  );
}

src/app/components/IntersectionObserver.tsx


.div 클래스
:

  • `height: 100vh` : 각 div 요소의 높이를 뷰포트 전체 높이로 설정하여, 화면을 가득 채우도록 합니다. 이로 인해 각 `div`는 화면에 꽉 차게 렌더링됩니다.
import IntersectionObserver from "./components/intersection-observer/IntersectionObserver";

export default function App() {
  return (
    <main>
        <IntersectionObserver />
    </main>
  );
}

 

2) 화면에 windows 스타일 보여주기 (react95)

먼저 프로젝트 디렉터리에 `react95`와 `styled-components`를 설치합니다.

$ npm install react95 styled-components
import { useInView } from "react-intersection-observer";
import { Window as Window95, WindowContent, WindowHeader } from "react95";
import { buildInteractionObserverThreshold } from "./helper/threshold";

export const Windows = () => {
  const { ref, inView } = useInView({
    triggerOnce: true,
    threshold: buildInteractionObserverThreshold(), 
  });

  const animationStyle = {
    transform: inView ? "scale(1.5)" : "scale(0)",
    transition: "transform 0.5s ease-out",
  };

  return (
    <div ref={ref} style={animationStyle}>
      <Window95>
        <WindowHeader active={false} className="window-title">
          <span>oh-no.exe</span>
        </WindowHeader>
        <WindowContent>Surprise!</WindowContent>
      </Window95>
    </div>
  );
};

src/app/components/windows.tsx


useInView 훅 사용 :

  • `useInView` 훅은 요소가 뷰포트에 들어왔는지(혹은 나갔는지)를 감지하는 데 사용됩니다.
  • `ref`: 감지하고자 하는 요소에 참조를 연결하는 데 사용됩니다.
  • `inView`: 요소가 현재 뷰포트에 보이는 상태인지 여부를 나타내는 불리언 값입니다.
  • `triggerOnce`: true: 요소가 한 번 화면에 보이면 그 이후에는 다시 감지하지 않도록 설정합니다.
  • `threshold`: 요소의 얼마나 많은 부분이 뷰포트에 들어왔을 때 이벤트를 트리거할지를 설정합니다. 이 코드에서는 `buildInteractionObserverThreshold()` 함수를 통해 `threshold` 값을 생성합니다.

animationStyle 객체 :

 

  • `transform`: `inView` 값에 따라 요소의 크기를 조절합니다.
    • `inView`가 `true`이면 요소가 1.5배로 확대됩니다 (scale(1.5)).
    • `inView`가 `false`이면 요소의 크기가 0으로 설정되어 화면에서 보이지 않게 됩니다 (scale(0)).
  • `transition`: 애니메이션 효과를 부드럽게 하기 위해 `transform` 속성이 변경될 때 0.5초 동안 `ease-out` 애니메이션이 적용됩니다.
import IntersectionObserver from "./components/intersection-observer/IntersectionObserver";
import original from "react95/dist/themes/original";

export default function App() {
  return (
    <main>
      <ThemeProvider theme={original}>
        <IntersectionObserver />
      </ThemeProvider>
    </main>
  );
}

 

 

3) threshord

이 함수는 특정 요소가 뷰포트에 얼마나 들어와야 하는지를 결정하는 데 사용할 수 있습니다.

export const buildInteractionObserverThreshold = (count = 100) => {
  const threshold = [];

  const parts = 1 / count;

  for (let i = 0; i <= count; i++) {
    threshold.push(parseFloat((parts * i).toFixed(2)));
  }

  return threshold;
};

`count` : 생성할 임계값의 수를 나타냅니다. 기본값은 100입니다. 즉, 기본적으로 100개의 임계값을 생성합니다.

`threshold` : 생성한 임계값을 저장할 배열입니다.

`parts` : 1을 `count`로 나눈 값입니다. 예를 들어, `count`가 100이면 `parts`는 0.01입니다. 이는 임계값 간의 간격을 설정하는 데 사용됩니다.

 

예를 들어, count가 100인 경우:

  • 첫 번째 임계값은 0.00입니다 (parts * 0).
  • 두 번째 임계값은 0.01입니다 (parts * 1).
  • ...
  • 마지막 임계값은 1.00입니다 (parts * 100).

 

 

 

참고

https://react-intersection-observer.vercel.app/

https://storybook.react95.io/?path=/story/docs-welcome-to-react95--page

반응형