사용 기술 스택: `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
'프론트엔드 > Library' 카테고리의 다른 글
[Library] React와 NextUI를 활용한 커스텀 셀렉터 컴포넌트 구현 가이드 (feat. TailwindCSS) (0) | 2024.08.26 |
---|