문제
height가 100vh인 컴포넌트를 여러개 렌더링하여 onViewportEnter로 뷰포트 감지를 하였는데 마운트 시에 한 개만 뷰포트에 감지되는 것이 아닌 두 개의 컴포넌트가 뷰포트에 감지됨으로써 뷰포트에 감지되었을 때 애니메이션을 작동하려는 의도와는 다르게 코드가 동작함
해결방안
react-intersection-observer 라이브러리를 사용하여 뷰포트 진입을 보다 정확하게 감지하여 해결했다.
간단히 요약하자면 해당 라이브러리를 사용해 컴포넌트가 뷰포트 50%이상 진입 시 뷰포트 감지로 간주하여 문제를 해결했다
초기 렌더링 시 0번째와 1번째 페이지의 애니메이션이 모두 실행되는 이유는, 두 컴포넌트가 뷰포트에 동시에 노출되기 때문일 수 있습니다. 특히, 스크롤 스냅이 적용된 경우 뷰포트의 크기와 스크롤 위치에 따라 여러 컴포넌트가 동시에 뷰포트에 노출될 수 있습니다.
이 문제를 해결하기 위해 `react-intersection-observer` 라이브러리를 사용하여 뷰포트에 진입하는 요소를 보다 정확하게 감지할 수 있습니다. 이 라이브러리는 Intersection Observer API를 사용하여 요소가 뷰포트에 진입하거나 이탈할 때 콜백을 트리거합니다.
먼저, `react-intersection-observer`를 설치합니다:
```bash
npm install react-intersection-observer
```
그리고, 페이지 컴포넌트에서 이 라이브러리를 사용하여 뷰포트 진입을 감지합니다:
### 수정된 코드 예제
```jsx
import { motion } from "framer-motion";
import { useState } from "react";
import styled from "styled-components";
import { useInView } from "react-intersection-observer";
export default function Content() {
const pageList = ["페이지 1", "페이지 2", "페이지 3", "페이지 4", "페이지 5"];
return (
<>
<Container>
{pageList.map((item, index) => (
<Page key={index} pageNum={item} pageIndex={index} />
))}
</Container>
</>
);
}
const Container = styled.div`
background-color: red;
width: 100vw;
height: 100vh;
overflow-y: scroll; // 스크롤이 가능하도록 설정
scroll-snap-type: y mandatory;
`;
function Page({ pageNum, pageIndex }) {
const itemList = [`아이템1`, `아이템2`, `아이템3`, `아이템4`, `아이템5`];
const [itemAnimation, setItemAnimation] = useState("stop");
const { ref, inView } = useInView({
triggerOnce: true, // 한 번만 트리거
threshold: 0.5, // 뷰포트에 50% 이상 보일 때 트리거
});
if (inView && itemAnimation === "stop") {
setItemAnimation("start");
}
return (
<PageWrap ref={ref}>
<Contents>
<h1>{pageNum}</h1>
{itemList.map((item, index) => (
<motion.div
key={index}
initial={{ x: "100vw" }}
animate={itemAnimation === "start" ? { x: 0 } : {}}
transition={{
type: "spring",
stiffness: 30,
delay: 0.5 * index, // 애니메이션 시작 전에 0.5초 딜레이
}}
>
{item}
</motion.div>
))}
</Contents>
</PageWrap>
);
}
const PageWrap = styled(motion.div)`
width: 100vw; // 화면 전체를 채운다
height: 100vh; // 화면 전체를 채운다
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
`;
const Contents = styled.div`
width: 300px;
height: 400px;
background-color: white;
position: relative;
scroll-snap-align: center; // parallax 효과
`;
```
### 설명
1. **`useInView`**: `react-intersection-observer`에서 제공하는 훅을 사용하여 요소의 뷰포트 진입 상태를 감지합니다.
2. **`ref`**: 감지할 요소에 연결되는 참조입니다.
3. **`inView`**: 요소가 뷰포트에 진입했는지 여부를 나타내는 불리언 값입니다.
4. **`triggerOnce`**: 요소가 한 번 뷰포트에 진입할 때만 트리거되도록 설정합니다.
5. **`threshold`**: 요소가 뷰포트에 50% 이상 보일 때 트리거되도록 설정합니다.
6. **`ref` 속성**: `PageWrap` 컴포넌트에 `ref`를 전달하여 뷰포트 진입 상태를 감지합니다.
7. **조건부 애니메이션 트리거**: `inView`가 `true`이고 `itemAnimation`이 `stop` 상태일 때 `setItemAnimation("start")`를 호출하여 애니메이션을 시작합니다.
이렇게 하면 뷰포트에 진입한 페이지에 대해서만 애니메이션이 실행되며, 0번째와 1번째 페이지가 동시에 애니메이션되는 문제를 해결할 수 있습니다.
'라이브러리 > framer-motion' 카테고리의 다른 글
[framer-motion] 페이지 전환 애니메이션 (0) | 2024.08.11 |
---|---|
[framer-motion] 뷰포트 인-아웃 감지 (1) | 2024.06.09 |
[framer-motion] 조건부 애니메이션 (0) | 2024.06.09 |
[Framer motion] 프레이머 모션(Framer motion) 상태값 변화없음으로 인한 애니메이션 중단 버그 해결 방법 (0) | 2024.01.19 |
[Framer] 프레이머 모션 인뷰 훅 사용법 (0) | 2024.01.18 |