네비게이션 바가 특정 위치만큼 내려왔을 때 CSS를 변경해야 하는 순간이 있다.
window.addEventListener('scroll', () => console.log(window.scrollY));
이러면 스크롤이 되는 매 순간마다 함수가 호출되기 때문에 이를 최적화 해줄 필요가 있다.
보통은 throttle 함수를 사용하여 최적화를 했으나 이번엔 새로운 방식으로 최적화를 하려고 한다.
쓰로틀 함수
export default function throttle(fn, threshhold, scope) {
threshhold || (threshhold = 250);
let last, deferTimer;
return function () {
let context = scope || this;
let now = +new Date(),
args = arguments;
if (last && now < last + threshhold) {
// hold on to it
clearTimeout(deferTimer);
deferTimer = setTimeout(function () {
last = now;
fn.apply(context, args);
}, threshhold);
} else {
last = now;
fn.apply(context, args);
}
};
}
window.addEventListener('scroll', throttle(onScroll, 300))
이유는 자바스크립트의 동작방식에 있는데,
싱글 스레드로 동작하는 JavaScript는 setTimeout API의 비동기 task들을 Task Queue에 넣어둔 후 순차적으로 처리합니다. Queue에 저장된 비동기 task를 처리하는 시점은 Call stack이 비어져있을 경우 입니다. 이 시점이 setTimeout 또는 setInterval에 할당해준 delay와 맞지 않는다면 등록해둔 callback은 trigger 되지 않을 수 있습니다.
다시말해 300ms가 지난 시점에서 Call stack에서 다른 함수들을 처리하고 있다면, 그 함수가 끝난 뒤에서야 스크롤 함수가 동작 할 것이기 때문이다. 또한 기존의 방식은 new Date() 를 지속적으로 호출하게 되며 이는 불필요한 callstack을 지속적으로 생성한다. 화면에 많은 그리기 동작을 애니메이션으로 구현해도 실제로 표시 가능한 주사율에 영향을 받기에 지나치게 높은 부하가 발생할 수 있다.
requestAnimationFrame? (rAF)
rAF는 자바스크립트의 내장 메소드이다. 이를 사용하게 되면 브라우저가 렌더링 할 수 있는 ‘능력’에 맞춰 이벤트를 트리거 할 수 있다. 즉 일부러 300ms 씩 trigger 하려고 하지 않아도 된다는 뜻. 브라우저는 60fps(초당 60회)로 화면을 렌더링, 이 렌더링에 최적화하기 위해 rAF 이라는 API를 사용할 수 있다.
이 rAF API도 setTimeout 과 마찬가지로 callback으로 넘겨지는 function을 비동기 task로 분류하여 처리. 다만 rAF 은 macro queue가 아니라 animation frame에서 처리된다. 또한 setTimeout 두번째 parameter로 전달되는 delay 값이 브라우저 렌더링에 최적화되어 있다는 차이가 있다.
export function toFit(
cb,
{ dismissCondition = () => false, triggerCondition = () => true }
) {
if (!cb) {
throw Error('Invalid required arguments')
}
let tick = false
return function() {
if (tick) {
return
}
tick = true
return requestAnimationFrame(() => {
if (dismissCondition()) {
tick = false
return
}
if (triggerCondition()) {
tick = false
return cb()
}
})
}
}
위의 코드를 설명하자면,
1. 스크롤이 될 때 발생시킬 함수 cb를 인자로 받음
2. dissmissCondition, triggerCondition 함수를 옵션으로 받아 트리거를 원하는 상황에 대한 핸들링을 해 준다
3. tick 변수는 클로저로, 이를 통해 브라우저가 렌더링 할 수 있는 능력 이상의 callback 함수 호출을 막는다.
4. requestAnimationFrame 메서드의 콜백 인자는 앞서 언급한 macro queue가 아니라 animation frame에서 처리된다.
5. 실제로 실행되기 전 까지는 tick이 true 이므로 tigger가 아무리 호출되도 실행이 되지 않는다.
6. 함수가 event loop에 의해 실행된다. 이때 tick은 false로 바뀌면서 실제 동작을 한다.
본글은
https://jbee.io/web/optimize-scroll-event/ https://developer.mozilla.org/ko/docs/Web/API/Window/requestAnimationFrame
를 인용하여 구성되었습니다.
window.requestAnimationFrame() - Web API | MDN
화면에 새로운 애니메이션을 업데이트할 준비가 될때마다 이 메소드를 호출하는것이 좋습니다. 이는 브라우저가 다음 리페인트를 수행하기전에 호출된 애니메이션 함수를 요청합니다. 콜백의
developer.mozilla.org
'[실무개발] > Frontend' 카테고리의 다른 글
[1] 장고(4버전) 관리자 커스텀 - 썸네일 확대 (0) | 2022.11.01 |
---|---|
리액트 hydration과 리액트쿼리의 dehydration (0) | 2022.10.21 |
State 에 따른 styled component 사용 (0) | 2022.04.05 |