본문 바로가기
Javascript

[Javascript] requestAnimationFrame(애니메이션 최적화하기)

by 슥짱 2022. 4. 25.

애니메이션을 그리기 위해서는 함수를 반복 호출하며 화면을 다시 그려야 한다. 이를 위해 쉽게 생각해볼 수 있는 방법은 setTimeout 또는 setInterval과 같은 스케줄링 함수를 사용하는 것이다.

setTimeout과 setInterval의 한계

그러나 이 함수들은 완벽한 애니메이션을 그리기엔 한계가 있다. 일반적인 모니터의 가변 주사율이 60프레임을 고려해, 1초에 60번의 호출이 발생하도록 delay를 1000/60ms로 준다고 가정하자. 보통의 경우는 이 delay가 잘 지켜져 의도한 대로 60 프레임의 애니메이션이 그려질 것이다. 그런데 만약 연산에 delay 이상의 시간이 필요한 경우는 어떨까? 싱글 스레드인 자바스크립트의 특성상, 주어진 delay 시간을 지키지 못하고 일부 애니메이션이 누락될 것이다.

window.requestAnimationFrame()

브라우저에서는 애니메이션을 그리기 위해 더 적합한 API인 requestAnimationFrame(이하 rAF)를 제공한다. rAF는 브라우저에 의해 repaint 직전에 실행돼, 60 프레임 또는 모니터의 가변 주사율에 최대한 맞추어 콜백 함수를 호출하기 때문에, 브라우저의 paint 사이클에 상관없이 실행되는 setTimeout, setInterval보다 부드러운 애니메이션을 제공할 수 있다.

 

아래의 코드를 개발자 도구에서 실행해보면, 실제로 초당 60회 정도의 속도로 "Hello"가 출력되는 것을 확인할 수 있을 것이다.

function draw() {
  console.log("hello");
  requestAnimationFrame(draw);
}

draw();

 

또한 rAF의 콜백 함수는 setTimeout 등에 의해 등록되는 Task Queue보다 우선 순위가 높은 Animation Frames라는 큐에 등록된다. 다수의 브라우저에서 Animation Frames의 작업들이 Task Queue의 작업들 보다 먼저 수행되며, 호출 스택이 빌 때 하나의 작업씩 꺼내오는 Task Queue와 달리 Animation Frames는 쌓여있는 모든 함수를 꺼내오기 때문에 더 부드러운 애니메이션을 제공할 수 있다.

 

뿐만 아니라 rAF는 백그라운드 탭으로 이동했을 때 자동으로 실행을 중단하는 기능이 있어, 성능과 배터리 수명에 더욱 유리하기도 하다.

예시

rAF는 mousemove나 drag와 같이 한 번에 많은 함수 호출이 일어날 수 있는 이벤트 리스너에도 활용할 수 있다. 아래 코드는 특별한 장치 없이 매 mousemove 움직임마다 animation을 호출하기 때문에, 가벼운 마우스 움직임에도 초당 60회 를 훌쩍 넘는 함수 호출이 발생한다. 이렇게 빠른 함수 호출은 모니터의 가변 주사율을 넘어서기 때문에 무의미한 연산으로 자원을 낭비할 뿐이다.

useEffect(() => {
    const animation = (e: MouseEvent) => {
        // do something
    }

    window.addEventListener('mousemove', animation);

    return () => window.removeEventListener('mousemove', animation);
  }, []);

 

rAF를 활용하면 위의 코드를 다음과 같이 최적화할 수 있다. 이제 마우스를 아무리 빨리 움직여도 rAF의 콜백 함수는 모니터의 가변 주사율 이상 호출되지 않는다.

useEffect(() => {
    let animating = false;

    const animation = (e: MouseEvent) => {
      if (animating) return;

      animating = true;
      return requestAnimationFrame(() => {
       //do something
       animating = false;
    };

    window.addEventListener('mousemove', animation);

    return () => window.removeEventListener('mousemove', animation);
  }, []);

 


참고자료

https://marshallku.com/web/tips/%EC%8A%A4%ED%81%AC%EB%A1%A4-%EB%93%B1%EC%9D%98-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%B5%9C%EC%A0%81%ED%99%94%ED%95%98%EA%B8%B0

 

스크롤 등의 이벤트 최적화하기

사용자의 스크롤, 클릭 등에 반응하는 페이지를 만들면, 1초에도 몇십 번씩 특정 함수가 동작해야 할 때가 많습니다.간단한 애니메이션을 출력하는 정도라면 상관없겠지만, 복잡한 그래픽 연산

marshallku.com

https://iamsjy17.github.io/javascript/2019/07/20/how-to-works-js.html

 

Songlog

Javascript, Typescript, Angular, React, RxJS, etc.

iamsjy17.github.io

https://developer.mozilla.org/ko/docs/Web/API/window/requestAnimationFrame

 

window.requestAnimationFrame() - Web API | MDN

화면에 새로운 애니메이션을 업데이트할 준비가 될때마다 이 메소드를 호출하는것이 좋습니다. 이는 브라우저가 다음 리페인트를 수행하기전에 호출된 애니메이션 함수를 요청합니다. 콜백의

developer.mozilla.org

 

댓글