requestAnimationFrame, cancelAnimationFrame

LESA·2023년 2월 8일
0
post-thumbnail

requestAnimationFrame 의 간단한 사용법과 왜 사용하는지에 대하여 알아보자


requestAnimationFrame ?

브라우저가 변화된 화면을 그릴 준비가 됐을때, 최적화를 하여 애니메이션을 부드럽게 표현할 수 있는 기술


  const rafCount = document.querySelector('.rafCount');
  const rocket = document.querySelector('.rocket');
  let rafValue;

  const loop = () => {
    rafValue = requestAnimationFrame(loop);
    rafCount.innerHTML = `requestAnimationFrame return value is ${rafValue}`;
    rocket.style.transform = `translateY(${-rafValue}px)`;

    if (rafValue >= 550) cancelAnimationFrame(rafValue);
  };

  loop();

위의 코드는 아래에서 보여지는 동작에대한 코드이다.
기본 적으로 사용법은 어떻게 보면 재귀함수의 형태와 비슷하다.
requestAnimationFrame 의 return 값은 정수이고 짧은 시간에 수많은 값을 반환한다.
이는 초당 60프레임을 목표로한다. 하드웨어마다 성능이 다르므로, 60프레임을 목표로한다.
반환 되는 값이 정수이므로 조건을 통하여 함수의 종료도 가능하다.
함수를 종료시키고 싶으면, cancelAnimationFrame 을 사용하면된다.


어떤 상황에서 쓰이는가 ?

위의 동작은 requestAnimationFrame 을 사용하지 않아도 충분히 구현할 수 있다.
그럼 어떤 상황에서 쓰이는지 알아보자.


노랑색은 requestAnimationFrame 을 적용하지 않은
초록색은 requestAnimationFrame 을 적용한
동작은 scroll event 에서 이루어지며, 키보드 방향키를 연타하여 이벤트를 발생시켰다.
초록색이 노란색보다 부드러운 것을 한눈에 알아볼 수 있다.

requestAnimationFrame 을 적용한 코드를 살펴보자.
해당 애니메이션은 속도를 감속시키는 계산식을 적용한 것이다.
(목표 지점 - 현재 지점) * 0.1 계산식을 설명해보자면, 목표 지점에서 현재 지점을 빼면 총 이동거리가 나오게되고 그 값에 10% 를 구한 것이다.
총 이동 거리는 계속 줄어들 것이고, 결국 0에 수렴할 것이다.
이러한 계산식으로 감속하는 애니메이션을 구현한다.

  const box = document.querySelector('.box'); // orange
  const box2 = document.querySelector('.box2'); // green


  // requestAnimationFrame 을 줄여서 raf 라고 칭했다.
  let acc = 0.1;
  let delayedYOffset = 0;
  let rafId;
  let rafState;

  window.addEventListener('scroll', () => {
    box.style.width = `${window.pageYOffset}px`;

    // cancelAnimationFrame 으로 함수를 취소하고나서, scroll event 가 발생할 때 다시 raf 를 실행시키는 로직
    // rafState 가 false, 즉 raf 가 실행되고 있지 않다면 다시 loop 함수를 인자로 받은 raf 를 실행
    if (!rafState) {
      rafId = requestAnimationFrame(loop);
      rafState = true;
    }
  });

  function loop() {
    delayedYOffset = delayedYOffset + (pageYOffset - delayedYOffset) * acc;
    box2.style.width = `${delayedYOffset}px`;

    rafId = requestAnimationFrame(loop);

    // 현재위치 보다 - 되는 경우를 방지하기 위해 절대값 처리
    // 목표 지점 - 현재 위치의 값이 1보다 작을경우 멈추게
    if (Math.abs(pageYOffset - delayedYOffset) < 1) {
      // raf 를 취소시킬 때, rafState 를 false 로 값을 할당.
      cancelAnimationFrame(rafId);
      rafState = false;
    }
  }

  loop();

결론

위와 같은 로직자체가 하나의 패턴이다. (부드러운 감속 애니메이션)
자주 사용될 수 있으니 알아두면 좋다.


유튜브 1분 코딩 - requestAnimationFrame

profile
Always All ways

0개의 댓글