requestAnimationFrame
은 브라우저에 수행하기를 원하는 애니메이션을 알리고 다음 리페인트가 진행되기 전에 해당 애니메이션을 업데이트하는 함수를 호출한다.
const callback = () => {
requestAnimationFrame(callback);
};
callback();
requestAnimationFrame
과 비교할 수 있는 방법으로는 setInterval
이 있다. 특정 애니메이션을 60FPS를 기준으로 업데이트한다면 코드는 다음과 같을 것이다.
setInterval(callback, 1000 / 60);
간단한 로딩바를 setInterval
로 구현해보자.
const sitpercent = document.querySelector("div#setinterval > div#sit-percent");
const sitprogress = document.querySelector(
"div#setinterval > div#sit-loading > div#sit-progress"
);
let sitAnimation = null;
const sitCallback = () => {
let width = sitprogress.style.width;
if (!width) {
sitprogress.style.width = "0%";
width = sitprogress.style.width;
}
const loadingPercent = parseInt(width.replace("%", ""), 10);
if (loadingPercent <= 100) {
const nextWidth = loadingPercent + Math.floor(100 / 60);
const updatedWidth = `${nextWidth >= 100 ? 100 : nextWidth}%`;
sitprogress.style.width = updatedWidth;
sitpercent.textContent = updatedWidth;
} else {
clearInterval(sitAnimation);
}
};
sitAnimation = setInterval(sitCallback, 1000 / 60);
이번에는 requestAnimationFrame
로 구현해보자.
const rafpercent = document.querySelector(
"div#requestanimationframe > div#raf-percent"
);
const rafprogress = document.querySelector(
"div#requestanimationframe > div#raf-loading > div#raf-progress"
);
let rafAnimation = null;
const rafCallback = () => {
let width = rafprogress.style.width;
if (!width) {
rafprogress.style.width = "0%";
width = rafprogress.style.width;
}
const loadingPercent = parseInt(width.replace("%", ""), 10);
if (loadingPercent <= 100) {
const nextWidth = loadingPercent + Math.floor(100 / 60);
const updatedWidth = `${nextWidth >= 100 ? 100 : nextWidth}%`;
rafprogress.style.width = updatedWidth;
rafpercent.textContent = updatedWidth;
rafAnimation = requestAnimationFrame(rafCallback);
} else {
cancelAnimationFrame(rafAnimation);
}
};
rafCallback();
결과물은 다음과 같다.
setInterval
vs requestAnimationFrame
두 방법의 차이점은 setInterval
은 프레임을 신경쓰지 않지만 requestAnimationFrame
은 프레임에 맞춰 애니메이션을 실행시킨다는 것이다.
애니메이션이 부드럽게 실행되려면 60FPS로 실행되어야하므로 약 16.6ms
에 하나의 프레임이 보여져야한다. 자바스크립트는 싱글스레드이므로 setInterval
의 경우 매크로 태스크 큐에 됐을 때 콜 스택 또는 태스크 큐 앞의 작업이 시간이 오래 걸리는 작업일 경우 프레임 업데이트가 늦어져 애니메이션이 부드럽게 실행되지 않는다.
requestAnimationFrame
은 60FPS로 애니메이션이 실행되는 것을 보장하기 위해 약 16.6ms
가 지난 후에 프레임 업데이트의 여유가 있다면 콜백 함수를 실행한다.
물론 DOM 조작과 관련없는 복잡한 작업을 Web Worker
에서 처리하도록 구현한다면 setInterval
의 문제를 보완할 수 있다. 하지만 이 경우에도 애니메이션 콜백 함수의 실행시간이 오래 걸릴 경우 해결되지 않는다. 그러므로 복잡한 애니메이션을 실행할 때는 requestAnimationFrame
을 사용할 것을 권장한다.