[js] 웹 애니메이션 requestAnimationFrame

Phillip Summer·2023년 7월 25일
0

웹 애니메이션도 웹에서 정말 많이 사용하는 기술 중 하나입니다.
이전에는 그냥 타이머함수 (setInterval(), setTimeout())를 사용해서 제작하였었는데,
웹을 백그라운드에 두거나 아니면 오랜 시간이 지나게되면 무한 롤링되거나 버벅되기 일수였습니다.
타이머함수의 문제점들을 타파하기 위해 나온것이 requestAnimationFrame라고 합니다.

🎀 requestAnimationFrame

🔍 rAF를 사용할 이유

setInterval() 와 setTimeout()인 타이머함수의 문제점은 주어진 시간내에 동작을 할 뿐 프레임을 신경 쓰지 않고 동작한다는 점이었습니다. 타이머 함수는 프레임 단위로 프레임 시작 시간에 맞춰 실행됨을 보장하지 못하기 때문였죠.
프레임을 보정받지 못하고 프레임이 깎인다는 것은 곧 프레임 드랍이 일어나 결국 화면이 버벅이게 됩니다.

뿐만아니라, 타이머함수는 브라우저의 다른 탭 화면을 보거나 브라우저가 최소화되어 있을 때 계속 타이머가 돌아 콜백을 호출하기 때문에 시스템 리소스 낭비를 초래하고 불필요한 전력을 소모하게 만들며, 더나아가 대기시간 없이 무한반복(롤링)까지 초래하게 됩니다.
반면 requestAnimationFrame는 페이지가 비활성화 된 상태이면 페이지 화면 그리기 작업도 브라우저에 의해 일시 중지됨으로 CPU 리소스나 배터리 수명을 낭비하지 않게 합니다.

🔍 rAF 기본 레이아웃

let raf; // requestAnimationFrame을 담을 변수

const draw = () => {
    /* 반복 실행할 스크립트 */
	
    // 특정 조건일 경우 반복 종료
    if(조건) {
    	cancelAnimationFrame(raf);
		return;
    }
    raf = requestAnimationFrame(draw) // 함수 내부에서 다시 requestAnimationFrame을 호출하여 반복
}
requestAnimationFrame(draw);

🔍 rAF 타이머 사용

하지만 제가 원하는건 setInterval과 setTimeOut의 대체였습니다. 보통 일정시간 반복, 혹은 종료의 경우에 사용하기 때문에, 위처럼 몇번이 아닌 타이머의 기준으로 수정해보았습니다.

let count = 0;
let raf;
function draw(){
    count++;
    if ( count % 120 === 0) { // ?초마다 실행 (1s = 60) -> 60프레임마다 1초를 의미
        console.log('go');
    }
    if (count >= 3600){ // ?분이 되면 종료 (1분 = 60 x 60) -> 3600프레임 = 60초 = 1분
        cancelAnimationFrame(raf);
        return;
    }

    raf = requestAnimationFrame(draw);
}
requestAnimationFrame(draw);

위에 메모한 것처럼 60프레임을 1초라고 생각하면 편합니다.
( count % 120 === 0) 부분을 풀어서 쓰면
count가 1프레임마다 증가하는데, 120프레임(약 2초)마다 반복실행한다고 보면 됩니다. 그리고 3600프레임(약 1분)이 넘어서면 반복을 종료합니다.

🔍 rAF 주의사항

하지만 여기서 맹점이 있으니..
60프레임이 반드시 1초라는건 아니라는 점입니다.
실제로 대부분의 최신 웹 브라우저에서 requestAnimationFrame 함수는 가능한 한 초당 60회에 가까운 콜백 함수를 호출하여 초당 약 60프레임(60 FPS)을 달성하려고 하지만, 정확히 말하자면 자신의 모니터가 60hz 주사율 모니터일 경우입니다. 즉, 웹브라우저는 디스플레이의 주사율을 따르며 만일 144hz 주사율 고사양 모니터일 경우 rAF 역시 1초에 144번 호출되게 됩니다.
setInterval 에서는 콜백 함수 호출 간격을 시간을 지정해주고 호출 횟수를 설정하지만, rAF에서는 모니터의 주사율을 그대로 따르게 되어 최적화 되어 있다는 겁니다. 그리고 이러한 특성 때문에 rAF는 스크롤 이벤트 최적화 기법으로도 쓰이기도 합니다.

때문에 예를 들어 그냥 디자인적으로 '약 3초정도마다 반복 실행했으면 좋겠어'정도로 생각하고 사용한다면, 정확한 3초는 아닐지라도, 만약 2.89초정도라도 정확하게 2.89초마다 반복하기를 원한다면 requestAnimationFrame을 사용하면 됩니다.
하지만 딱 정확한 3초, 3.00초가 필요한거라면 결국 requestAnimationFrame() 내에 타이머함수를 사용하는 수밖에 없습니다. 다행히도 rAF 내에 타이머함수를 사용해도 기존의 타이머함수만 사용할때처럼 백그라운드 실행 같은 일은 없습니다.

👓 참고한 문헌

profile
이번여름부터 다시 시작한 개발자

1개의 댓글

comment-user-thumbnail
2023년 7월 25일

글 잘 봤습니다.

답글 달기

관련 채용 정보