스크롤에 따라 텍스트가 나타나는 효과 구현하기
(Feat. getBoundingClientRect, useRef)
1. 화면에 보여줄 텍스트 라인을 보여줄 상태 생성하기
[
"첫 번째 문장",
"두 번째 문장",
"세 번째 문장",
"네 번째 문장",
"다섯 번째 문장",
]
- 각 문장들을 배열에 저장한다.
// 스크롤 위치에서 보일 텍스트 index를 저장할 배열
const [visibleLines, setVisibleLines] = useState<number[]>([]);
- 현재 스크롤이 위치한 화면에서 보여줄 텍스트의 index를 저장할 number 빈 배열을 만들어 준다.
- 예를 들어, 두 번째 문장 까지 보여준다면 [0, 1] 이 저장될 것이다.
2. useRef를 사용하여 이 텍스트를 보여주는 컴포넌트(요소)를 저장한다.
const textRef = useRef<HTMLDivElement>(null); // Text div에 접근할 Ref
- 나는 <Text> div 컴포넌트에 각 라인들을 <TextLine> 으로 보여주고 있으므로 해당 div 컴포넌트에 Ref 를 지정해줬다.
3. 스크롤 이벤트 함수 만들어주기
// 스크롤 위치에 따라 한 줄씩 텍스트라인이 보이게 하는 함수
const handleScroll = () => {
if (textRef.current) {
// Text div가 top 에서 얼마나 떨어져 있는지, 스크롤 할 때마다 변경
const { top } = textRef.current.getBoundingClientRect();
// 화면 상단 90%를 지정. 즉, 화면 하단 10% 지점에서부터 텍스트가 나타나게끔. 화면의 높이 기준이므로 고정
const scrollThreshold = window.innerHeight * 0.9;
// 화면에 보일 텍스트라인을 저장할 배열
const newVisibleLines = [];
// 현재 스크롤 한 시점의 top이 지정한 높이를 넘었으면 배열에 텍스트라인을 넣어준다.
// 한 줄당 56px정도 차를 주어 스크롤을 내릴 때 마다 한 줄씩 나타나는 느낌을 주도록 한다.
for (let i = 0; i < introText.desc.length; i++) {
if (top + i * 56 < scrollThreshold) {
newVisibleLines.push(i);
}
}
setVisibleLines(newVisibleLines);
}
};
- textRef.current.getBoundingClientRect() 함수는 지정한 요소의 현재 위치를 가져오는 함수이다.
- 그 중, top 요소만 사용할 것이기 때문에 top 변수를 저장해줬다.
- 그 후 스크롤 시점에 따라 visibleLines 상태 배열에 보여줄 텍스트 라인의 index를 저장해준다.
- 각 줄 마다 보여야 할 위치가 다르기 때문에 56px 정도 차를 주었다.
- 56px의 기준은 직접 스크롤 해보고, 가장 자연스러운 px을 지정해주었다.
4. 스크롤 이벤트 설정(useEffect)
// 스크롤 할 때마다 handleScroll 함수 실행
useEffect(() => {
window.addEventListener("scroll", handleScroll);
// 언마운트 시 리스너 제거는 꼭 해주자
return () => window.removeEventListener("scroll", handleScroll);
}, []);
- 스크롤 할 때마다, 위의 이벤트 함수를 실행해서 보여줄 텍스트 라인을 결정한다.
- 즉, 스크롤을 할 때마다, 현재 화면의 top 위치를 찾고, 이 위치와 내가 지정한 위치를 비교해서 몇 개의 텍스트 라인을 보여줄 지 결정한다!
5. 각 텍스트 라인을 순차적으로 보이게 하기
<Text className="text" ref={textRef}>
{introText.desc.map((line, index) => (
<TextLine key={index} isVisible={visibleLines.includes(index)}>
{line}
</TextLine>
))}
- 각 TextLine 컴포넌트를 만들어주고, visibleLines의 index에 TextLine의 index가 존재한다는 의미는 해당 TextLine을 보여준다는 의미이다.
- 예를 들어, 아까 두 번째 문장까지 보여준다면 visibleLines에 [0, 1]이 저장될 것이고, TextLine의 0, 1 index에 해당하는 두 번째 줄 까지만 isVisible이 true라는 의미이다!
const TextLine = styled.p<{ isVisible: boolean }>`
font-size: 16px;
opacity: 0; // 처음에는 보이지 않도록 설정
/* isVisible일 때에만 fallDown 애니메이션 적용 */
animation: ${(props) => props.isVisible && fallDown} 0.6s ease forwards;
`;
// 위에서 아래로 내려오는 애니메이션
const fallDown = keyframes`
from {
opacity: 0;
transform: translateY(-16px);
}
to {
opacity: 1;
transform: translateY(0);
}
`;
- 따라서, isVisible이 true일 때에만 fallDown 애니메이션을 실행해 주면 된다.
'[Study] 개발 공부 > [React] 리액트 공부' 카테고리의 다른 글
[React] react-copy-to-clipboard: 클립보드 복사 기능 구현하기 (0) | 2024.11.25 |
---|---|
[React] React Transition Group 사용하기 (0) | 2024.11.22 |
[React] 모바일에서는 메뉴가 버튼으로 바뀌는 반응형 헤더 만들어보기 (0) | 2024.11.04 |
styled-components+TS 에서 전역 스타일 변수 설정하기 (0) | 2024.09.10 |
[Vite+TS] alias 설정하기 (0) | 2024.09.06 |