보통 자바스크립트로 애니메이션을 구현해야 할 경우, setInterval과 requestAnimationFrame을 이용한 방식이 가장 많이 사용된다.
나는 setInterval을 주로 사용했었는데, setInterval로 만든 애니메이션의 경우에는 같은 애니메이션을 동시에 여러 번 호출하는 경우 애니메이션이 꼬이면서 버벅임이 발생할 수 있다는 단점이 있다.
(애니메이션 함수가 종료되기 전에 이벤트를 실행하는 경우에는 중첩돼서 이벤트가 실행되기 때문에 clearInterval로 기존 이벤트를 지워줘야 함)
그래서 이번에 requestAnimationFrame으로 코드를 리팩토링하면서 알게 된 setInterval과 requestAnimationFrame의 차이점을 간략히 정리했다.
setInterval
이벤트 지정
첫 번째 파라미터로 callback 함수, 두 번째 파라미터로 실행 간격(default = 0)을 전달한다. 1초의 경우, 1000을 전달하면 된다.
주의할 점은 callback 함수 내에 종료 조건을 명시하지 않으면 setInterval은 무한히 실행된다.
let t = 10;
let animation = null;
const callback = () => {
if (--t <= 0) clearInterval(animation);
console.log('callback', t);
}
animation = setInterval(callback, 1000);
이벤트 취소
clearInterval에 setInterval 함수를 담고 있는 변수를 전달한다.
clearInterval(animation);
사용 예시
donut.forEach((e, i) => {
let t = 0;
let w = Math.round(deptList[i].percent * 100);
let width = w + (i === idx2 ? 0 : Math.round(deptList[i].percent2 * 100));
const color = graphColor[i === idx2 ? 1 : 0];
const handleDonutAnimation = () => {
e.querySelector('.donut').style.background =
t <= width && i === idx2
? `conic-gradient(${color} 0% ${t}%, ${graphColor[3]} ${t}% 100%)`
: `conic-gradient(${color} 0% ${Math.min(t, w)}%, ${graphColor[2]} ${Math.min(t, w)}% ${Math.min(
t,
width,
)}%, ${graphColor[3]} ${Math.min(t, width)}% 100%)`;
t++ === width && clearInterval(donutAnimation);
};
const donutAnimation = setInterval(handleDonutAnimation, 10);
});
requestAnimationFrame
이벤트 지정
requestAnimationFrame 함수에 callback을 전달한다. setInterval 함수를 사용하여 애니메이션을 구현할 때와는 달리 callback 함수 내에서 requestAnimationFrame을 재호출해야 한다.
let t = 60;
const callback = () => {
console.log('callback', t--);
t && requestAnimationFrame(callback);
}
requestAnimationFrame(callback);
이벤트 취소
이벤트를 실행했을 때와 마찬가지로 callback 함수를 cancelAnimationFrame 함수에 전달하면 현재 실행중인 애니메이션을 취소할 수 있다.
cancelAnimationFrame(callback);
타임스탬프 사용
requestAnimationFrame 함수의 callback 함수의 매개변수로 timestamp 변수를 전달하면 requestAnimationFrame 함수가 실행되기 시작한 이후의 시간을 받을 수 있다.
let startTime = null;
function draw(timestamp) {
if(!startTime) {
startTime = timestamp;
}
currentTime = timestamp - startTime;
// Do something based on current time
requestAnimationFrame(draw);
}
draw();
사용 예시
donut.forEach((e, i) => {
let t = 0;
let w = Math.round(deptList[i].percent * 100);
let width = w + (i === idx2 ? 0 : Math.round(deptList[i].percent2 * 100));
const color = graphColor[i === idx2 ? 1 : 0];
const handleDonutAnimation = () => {
e.querySelector('.donut').style.background =
t <= width && i === idx2
? `conic-gradient(${color} 0% ${t}%, ${graphColor[3]} ${t}% 100%)`
: `conic-gradient(${color} 0% ${Math.min(t, w)}%, ${graphColor[2]} ${Math.min(t, w)}% ${Math.min(
t,
width,
)}%, ${graphColor[3]} ${Math.min(t, width)}% 100%)`;
t++ !== width && requestAnimationFrame(handleDonutAnimation);
};
requestAnimationFrame(handleDonutAnimation);
});
setInterval과 requestAnimationFrame의 차이점
둘의 차이점은 다음과 같다.
1. 속도 조절 가능 여부
setInterval은 속도를 조절할 수 있는 반면, requestAnimationFrame은 속도 조절이 불가하다.
무조건 1초에 60번 실행된다고 한다. (≒ 0.0167초에 한 번 실행)
그 이유는 일반적인 모니터의 화면 재생률이 60Hz이기 때문이다.
2. 콜백 실행 방식
requestAnimationFrame에서는 다음 콜백을 실행하려면 반드시 콜백 함수 내에서 requestAnimationFrame을 재호출해야만 한다.
반면에 setInterval의 경우에는 clearInterval로 중지하지 않는 한 영원히 실행된다. 만약 애니메이션을 중지하고 싶다면, 해당 함수를 갖는 변수를 별도로 선언한 후, clearInterval 함수에 해당 변수를 파라미터로 전달하여 중지시켜야 한다.
만약 setInterval로 만든 애니메이션을 중지하지 않고 또 애니메이션 함수를 호출하는 경우에는 아래와 같이 애니메이션이 겹쳐져서 버벅임이 발생한다.
3. 프레임의 부드러움
setInterval으로 구현한 애니메이션은 약간의 프레임 끊김이 발생하거나 프레임 자체를 빠뜨리는 문제가 발생할 수 있다.
requestAnimationFrame은 애니메이션을 위해 최적화된 함수이므로 애니메이션이 실행되는 환경에 관계 없이 적절한 프레임 속도로 실행되며, 탭이 활성화되지 않은 상태이거나 애니메이션이 페이지를 벗어난 경우에도 계속 실행되는 기존의 문제점을 해결할 수 있는 방법이라고 한다.
4. IE에서의 지원 여부
requestAnimationFrame은 IE 10버전부터 지원한다.
따라서 IE 10버전 이하 브라우저도 지원하려면 setInterval을 사용하거나 polyfill로 requestAnimationFrame을 주입해야 한다.
'Language > Javascript' 카테고리의 다른 글
Javascript - axios vs fetch vs ajax (0) | 2021.12.15 |
---|---|
Javascript - script 로딩 시 async vs defer (0) | 2021.12.13 |
Javascript - 실행 컨텍스트와 스코프, 클로저 (0) | 2021.12.01 |
Javascript - this (0) | 2021.11.28 |
Javascript - 부모 창에서 window.open()으로 연 자식 창 관리 (1) | 2021.10.03 |