스크롤 이벤트 처리를 할 경우, 일반적인 순수 Y축 값을 통해 측정, setTimeout을 사용하여 throttle 처리, requestAnimationFrame
3가지 방법을 각각 비교하여 어떤 차이가 있는지 비교하자.
순수 스크롤 이벤트를 통한 Y축 값 측정
const [scrollFlag, setScrollFlag] = useState(false);
const handleScroll = () => {
const { scrollY } = window;
const isScrolled = scrollY !== 0;
// console.log(scrollY);
setScrollFlag(isScrolled);
};
useEffect(() => {
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, []);
스크롤을 내릴때 상당히 많은 값들이 측정된다.
만일 setState이 연쇄적으로 변경이 필요한 함수일 경우 불필요한 재렌더링이 동작되어
브라우저 성능에 악영향을 줄 수도 있다.
setTimeout을 이용한 throttle 처리
const [scrollFlag, setScrollFlag] = useState(false);
const updateScroll = () => {
const { scrollY } = window;
const isScrolled = scrollY !== 0;
console.log(scrollY);
setScrollFlag(isScrolled);
};
const throttle = (callback, delay) => {
let timer = null;
// 클로저 함수 발동
return () => {
// 중복 방지 (두번 셋타임 들어가 콜백 부르기에 비효율적 이므로)
if (timer) return;
timer = setTimeout(() => {
callback();
timer = null;
}, delay);
// console.log("###", timer);
};
};
const handleScroll = throttle(updateScroll, 100);
useEffect(() => {
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, []);
스크롤을 내릴 경우, 100ms 마다 throttle 함수가 동작되어 반환되므로,
빠른 속도로 실행되는 scroll 이벤트를 제어할 수 있다.
그러나 이 방법은 지난 블로그 포스트에서도 언급했듯,
사용자 모니터의 성능에 따라 100ms의 제어를 못할 수도 있게 된다.
물론 단순 Y축 값 0이 아닌 경우에 따라 css 효과가 단순하게 바뀌는 경우, throttle 처리는 큰 문제가 없다.
하지만, 애니메이션 효과가 연쇄적인 css 처리가 진행된다면 큰 문제가 발생한다.
requestAnimationFrame
const [scrollFlag, setScrollFlag] = useState(false);
const updateScroll = () => {
const { scrollY } = window;
const isScrolled = scrollY !== 0;
console.log(scrollY);
setScrollFlag(isScrolled);
};
const task = (callback) => {
let tick = false;
if (tick) {
return;
}
return () => {
tick = true;
return requestAnimationFrame(() => {
tick = false;
return callback();
});
};
};
const handleScroll = task(updateScroll);
useEffect(() => {
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, []);
모니터 hz 성능에 알맞도록 더 정교하게 측정되었다.
setTimeout처럼 임의로 시간을 안맞추어도 좀 더 자연스럽게 브라우저가 적절한 타이밍에 조절해주게 된다.
그러나 이 방법은 canvas, svg 등 복잡한 애니메이션 을 다룰 때 쓰이는 방법이지,
단지 스크롤 값을 일시적으로 측정하여 발생하는 트리거라면 오히려 setTimeout이 더 효과적이다.
requestAnimationFrame 사용한 Header 트리거 이벤트
너무 정교하게 잡은 까닭에 state 변경이 처리되기 전, y축 값이 여러번 측정되어 버벅이는 현상을 발견할 수 있다.