TIL - 5일차

박춘화·2022년 2월 3일
0

Today I Learn

목록 보기
5/5

requestAnimationFrame

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을 사용할 것을 권장한다.

출처

profile
함께 성장하는 개발자

0개의 댓글