"일꾼이 일을 잘하려면 먼저 도구를 갈고 닦아야 한다." - 공자, 『논어』.
첫 장 > 프로그램 작성 > 웹 애니메이션 강화: 전문가처럼 requestAnimationFrame 최적화

웹 애니메이션 강화: 전문가처럼 requestAnimationFrame 최적화

2024-11-06에 게시됨
검색:902

Supercharge Your Web Animations: Optimize requestAnimationFrame Like a Pro

부드럽고 성능이 뛰어난 애니메이션은 최신 웹 애플리케이션에 필수적입니다. 그러나 부적절하게 관리하면 브라우저의 메인 스레드에 과부하가 걸려 성능이 저하되고 애니메이션이 버벅거릴 수 있습니다. rAF(requestAnimationFrame)는 디스플레이의 새로 고침 빈도와 애니메이션을 동기화하도록 설계된 브라우저 API로, setTimeout과 같은 대안에 비해 더 부드러운 동작을 보장합니다. 그러나 rAF를 효율적으로 사용하려면 특히 여러 애니메이션을 처리할 때 신중한 계획이 필요합니다.

이 글에서는 애니메이션 관리를 중앙 집중화하고, FPS 제어를 도입하고, 브라우저의 메인 스레드의 응답성을 유지하여 requestAnimationFrame을 최적화하는 방법을 살펴보겠습니다.


FPS 이해와 그것이 중요한 이유

초당 프레임 수(FPS)는 애니메이션 성능을 논의할 때 매우 중요합니다. 대부분의 화면은 60FPS로 새로 고쳐집니다. 이는 requestAnimationFrame이 초당 60번 호출된다는 의미입니다. 부드러운 애니메이션을 유지하려면 브라우저는 프레임당 약 16.67밀리초 내에 작업을 완료해야 합니다.

단일 프레임 동안 너무 많은 작업이 실행되면 브라우저가 대상 프레임 시간을 놓치게 되어 끊김 현상이 발생하거나 프레임이 삭제될 수 있습니다. 특정 애니메이션의 FPS를 낮추면 메인 스레드의 부하를 줄여 성능과 부드러움 사이의 균형을 맞추는 데 도움이 될 수 있습니다.

더 나은 성능을 위해 FPS 제어 기능을 갖춘 중앙 집중식 애니메이션 관리자

애니메이션을 보다 효율적으로 관리하기 위해 여러 requestAnimationFrame 호출을 코드 전체에 분산시키는 대신 공유 루프를 사용하여 애니메이션 처리를 중앙 집중화할 수 있습니다. 중앙 집중식 접근 방식을 통해 중복 호출을 최소화하고 FPS 제어를 더 쉽게 추가할 수 있습니다.

다음 AnimationManager 클래스를 사용하면 대상 FPS를 제어하면서 애니메이션 작업을 등록 및 등록 취소할 수 있습니다. 기본적으로 60FPS를 목표로 하지만 성능 요구에 따라 조정될 수 있습니다.

class AnimationManager {
  private tasks: Set = new Set();
  private fps: number = 60; // Target FPS
  private lastFrameTime: number = performance.now();
  private animationId: number | null = null; // Store the animation frame ID

  private run = (currentTime: number) => {
    const deltaTime = currentTime - this.lastFrameTime;

    // Ensure the tasks only run if enough time has passed to meet the target FPS
    if (deltaTime > 1000 / this.fps) {
      this.tasks.forEach((task) => task(currentTime));
      this.lastFrameTime = currentTime;
    }

    this.animationId = requestAnimationFrame(this.run);
  };

  public registerTask(task: FrameRequestCallback) {
    this.tasks.add(task);
    if (this.tasks.size === 1) {
      this.animationId = requestAnimationFrame(this.run); // Start the loop if this is the first task
    }
  }

  public unregisterTask(task: FrameRequestCallback) {
    this.tasks.delete(task);
    if (this.tasks.size === 0 && this.animationId !== null) {
      cancelAnimationFrame(this.animationId); // Stop the loop if no tasks remain
      this.animationId = null; // Reset the ID
    }
  }
}

export const animationManager = new AnimationManager();

이 설정에서는 프레임 간 deltaTime을 계산하여 대상 FPS를 기반으로 다음 업데이트에 충분한 시간이 경과했는지 확인합니다. 이를 통해 브라우저의 메인 스레드가 과부하되지 않도록 업데이트 빈도를 조절할 수 있습니다.


실제 예: 서로 다른 속성을 가진 여러 요소에 애니메이션 적용

각각 다른 애니메이션을 사용하는 세 개의 상자에 애니메이션을 적용하는 예를 만들어 보겠습니다. 하나는 크기가 조정되고, 다른 하나는 색상이 변경되고, 세 번째 상자는 회전합니다.

HTML은 다음과 같습니다.

CSS는 다음과 같습니다.

.animated-box {
  width: 100px;
  height: 100px;
  background-color: #3498db;
  transition: transform 0.1s ease;
}

이제 JavaScript를 추가하여 각 상자에 다른 속성을 적용해 보겠습니다. 하나는 크기를 조정하고, 다른 하나는 색상을 변경하고, 세 번째는 회전합니다.

1단계: 선형 보간 추가(lerp)

선형 보간(lerp)은 애니메이션에서 두 값 사이를 원활하게 전환하는 데 사용되는 일반적인 기술입니다. 점진적이고 부드러운 진행을 만드는 데 도움이 되므로 시간이 지남에 따라 속성을 확장, 이동 또는 변경하는 데 이상적입니다. 이 함수는 시작 값, 종료 값, 전환이 얼마나 진행되는지 결정하는 정규화된 시간(t)의 세 가지 매개변수를 사용합니다.

function lerp(start: number, end: number, t: number): number {
  return start   (end - start) * t;
}

2단계: 애니메이션 크기 조정

첫 번째 상자의 크기 조정에 애니메이션을 적용하는 함수를 만드는 것부터 시작합니다.

function animateScale(
  scaleBox: HTMLDivElement,
  startScale: number,
  endScale: number,
  speed: number
) {
  let scaleT = 0;

  function scale() {
    scaleT  = speed;
    if (scaleT > 1) scaleT = 1;

    const currentScale = lerp(startScale, endScale, scaleT);
    scaleBox.style.transform = `scale(${currentScale})`;

    if (scaleT === 1) {
      animationManager.unregisterTask(scale);
    }
  }

  animationManager.registerTask(scale);
}

3단계: 컬러 애니메이션

다음으로 두 번째 상자의 색상 변경에 애니메이션을 적용합니다.

function animateColor(
  colorBox: HTMLDivElement,
  startColor: number,
  endColor: number,
  speed: number
) {
  let colorT = 0;

  function color() {
    colorT  = speed;
    if (colorT > 1) colorT = 1;

    const currentColor = Math.floor(lerp(startColor, endColor, colorT));
    colorBox.style.backgroundColor = `rgb(${currentColor}, 100, 100)`;

    if (colorT === 1) {
      animationManager.unregisterTask(color);
    }
  }

  animationManager.registerTask(color);
}

4단계: 회전 애니메이션

마지막으로 세 번째 상자를 회전하는 함수를 만듭니다.

function animateRotation(
  rotateBox: HTMLDivElement,
  startRotation: number,
  endRotation: number,
  speed: number
) {
  let rotationT = 0;

  function rotate() {
    rotationT  = speed; // Increment progress
    if (rotationT > 1) rotationT = 1;

    const currentRotation = lerp(startRotation, endRotation, rotationT);
    rotateBox.style.transform = `rotate(${currentRotation}deg)`;

    // Unregister task once the animation completes
    if (rotationT === 1) {
      animationManager.unregisterTask(rotate);
    }
  }

  animationManager.registerTask(rotate);
}

5단계: 애니메이션 시작

마지막으로 세 상자 모두에 대해 애니메이션을 시작할 수 있습니다.

// Selecting the elements
const scaleBox = document.querySelector("#animate-box-1") as HTMLDivElement;
const colorBox = document.querySelector("#animate-box-2") as HTMLDivElement;
const rotateBox = document.querySelector("#animate-box-3") as HTMLDivElement;

// Starting the animations
animateScale(scaleBox, 1, 1.5, 0.02); // Scaling animation
animateColor(colorBox, 0, 255, 0.01); // Color change animation
animateRotation(rotateBox, 360, 1, 0.005); // Rotation animation

메인 스레드에 대한 참고 사항

requestAnimationFrame을 사용할 때 애니메이션이 브라우저의 메인 스레드에서 실행된다는 점을 명심하는 것이 중요합니다. 작업이 너무 많은 기본 스레드에 과부하가 걸리면 브라우저가 애니메이션 프레임을 놓치고 끊김 현상이 발생할 수 있습니다. 이것이 바로 중앙 집중식 애니메이션 관리자 및 FPS 제어와 같은 도구를 사용하여 애니메이션을 최적화하면 여러 애니메이션에서도 부드러움을 유지하는 데 도움이 될 수 있는 이유입니다.


결론

JavaScript에서 애니메이션을 효율적으로 관리하려면 requestAnimationFrame을 사용하는 것 이상이 필요합니다. 애니메이션을 중앙 집중화하고 FPS를 제어하면 메인 스레드의 응답성을 유지하면서 더 부드럽고 성능이 뛰어난 애니메이션을 보장할 수 있습니다. 이 예에서는 단일 AnimationManager로 여러 애니메이션을 처리하는 방법을 보여주면서 성능과 유용성을 모두 최적화하는 방법을 보여주었습니다. 단순성을 위해 일관된 FPS를 유지하는 데 중점을 두었지만 이 접근 방식을 확장하여 다양한 애니메이션에 대해 다양한 FPS 값을 처리할 수 있습니다. 하지만 이는 이 문서의 범위를 벗어났습니다.

Github 저장소: https://github.com/JBassx/rAF-optimization
StackBlitz: https://stackblitz.com/~/github.com/JBassx/rAF-optimization

LinkedIn: https://www.linkedin.com/in/josephciullo/

릴리스 선언문 이 기사는 https://dev.to/josephciullo/supercharge-your-web-animations-optimize-requestanimationframe-like-a-pro-22i5?1에서 복제됩니다. 침해 사항이 있는 경우, [email protected]으로 문의해 주십시오. 그것을 삭제하려면
최신 튜토리얼 더>

부인 성명: 제공된 모든 리소스는 부분적으로 인터넷에서 가져온 것입니다. 귀하의 저작권이나 기타 권리 및 이익이 침해된 경우 자세한 이유를 설명하고 저작권 또는 권리 및 이익에 대한 증거를 제공한 후 이메일([email protected])로 보내주십시오. 최대한 빨리 처리해 드리겠습니다.

Copyright© 2022 湘ICP备2022001581号-3