해당 글은 김주성님의 세미나를 듣고 작성한 글 입니다.
보통 금액이나, 사용자 수 등 숫자와 관련된 내용에 Animation을 추가한다 하면 Count Animation을 많이 사용합니다. React Custom Hooks을 사용하면 Scroll에 반응하는 CountUp Animation을 재사용 가능하게 만들 수 있습니다.
스크롤 타이밍에 따라 숫자가 CountUp 되는 animation은 크게 3가지 부분으로 나눌 수 있습니다.
이전에 작성했던 React Custom Hooks로 scroll animation 만들기 FadeIn 포스트와 구현 방식이 동일하며, FadeIn Animation 대신 CountUp Animation 로직을 추가한다는 것만 다릅니다.
useScrollHook.js
라는 파일을 생성 후 다음과 같이 작성합니다.
const useScrollCount = () => {
const dom = useRef();
const handleScroll = useCallback(([entry]) => {
const { current } = dom;
if(entry.isIntersecting) {
// 원하는 이벤트를 추가 할 것
}
}, []);
useEffect(() => {
let observer;
const { current } = dom;
if (current) {
observer = new IntersectionObserver(handleScroll, { threshold: 0.7 });
observer.observe(current);
return () => observer && observer.disconnect();
};
}, [handleScroll]);
return {
ref: dom,
};
}
export default useScrollCount;
위의 코드는 IntersectionObserver
를 사용하여 지정된 element가 70%정도 노출 되었을 때 ref를 사용해 지정한 DOM에 이벤트를 발생 시키는 custom 훅 입니다.
구현 과정과 세부 내용들은 React Custom Hooks로 scroll animation 만들기 FadeIn 포스트의 ‘custom hook 생성하기’와 ‘Scroll 트리거 이벤트 만들기’를 봐주시면 감사하겠습니다.
이렇게 만들어진 Hook은 아래 코드처럼 사용 할 수 있습니다.
import React from 'react';
import { useScrollCount } from '@/hooks';
const Figure = () => {
const animatedItem = useScrollCount();
return (
<Wrapper>
<Title>Total Projects</Title>
<Description>
Consequat interdum varius sit amet mattis vulputate enim.
</Description>
<p {...animatedItem} />
</Wrapper>
)
}
Count Animation을 가장 쉽게 구현하는 방법은 setInterval
을 사용하는 것 입니다.
setInterval()
은 일정 시간마다 반복 실행하는 함수 인데요.
위와 같이 Slide 형태의 Image 목록을 만들고 정해진 시간 간격으로 움직이게 하거나, 일정 주기로 계속해서 서버와 통신이 필요한 경우 등과 같이 활용 할 수 있습니다.
setInterval()
함수의 기본적인 사용 방법은 아래와 같습니다.
const interval = setInterval("실행할 함수", "시간(ms)"); // 생성 시점부터 동작
clearInterval(interval) // 반복 동작을 취소하고 싶을 때
별도로 변수 선언 없이도 setInterval
함수 사용이 가능하지만, 변수에 따로 저장하면 추후 clearInterval
함수를 통해 원하는 타이밍에 취소 할 수 있습니다.
해당 함수를 이용하여 초기 값(start) 부터 목표 값(end), 원하는 애니메이션 시간(duration)을 받아와 CountUp을 구현 할 수 있습니다.
const stepTime = Math.abs(Math.floor(duration / (end - start))); // 1
if (entry.isIntersecting) {
let currentNumber = start;
const counter = setInterval(() => {
currentNumber += 1;
if (currentNumber === end) { // 2
clearInterval(counter);
}
}, stepTime); // 3
}
1
의 stepTime은 setInterval의 반복 시간을 설정하는데 사용됩니다.
N초 마다 숫자가 1씩 올라가야 목표 값에 도달 할 지 계산하여 해당 시간마다 1씩 올라가도록 3
번에 넣어줍니다.
2
의 경우 목표치에 도달 했을 때 clearInterval
을 사용하여 해당 이벤트를 중단 시키는데 사용됩니다.
최종 코드는 아래와 같습니다.
const useScrollCount = (end, start = 0, duration = 3000) => {
const dom = useRef();
const stepTime = Math.abs(Math.floor(duration / (end - start))); // 1
const handleScroll = useCallback(([entry]) => {
const { current } = dom;
if(entry.isIntersecting) {
let currentNumber = start;
const counter = setInterval(() => {
currentNumber += 1;
if (currentNumber === end) {
clearInterval(counter);
}
}, stepTime)
}
}, []);
useEffect(() => {
let observer;
const { current } = dom;
if (current) {
observer = new IntersectionObserver(handleScroll, { threshold: 0.7 });
observer.observe(current);
return () => observer && observer.disconnect();
};
}, [handleScroll]);
return {
ref: dom,
};
}
export default useScrollCount;