«Если рабочий хочет хорошо выполнять свою работу, он должен сначала заточить свои инструменты» — Конфуций, «Аналитики Конфуция. Лу Лингун»
титульная страница > программирование > Усовершенствуйте свою веб-анимацию: оптимизируйте requestAnimationFrame как профессионал

Усовершенствуйте свою веб-анимацию: оптимизируйте requestAnimationFrame как профессионал

Опубликовано 6 ноября 2024 г.
Просматривать:536

Supercharge Your Web Animations: Optimize requestAnimationFrame Like a Pro

Плавная и производительная анимация необходима в современных веб-приложениях. Однако неправильное управление ими может привести к перегрузке основного потока браузера, что приведет к снижению производительности и прерывистой анимации. requestAnimationFrame (rAF) — это API браузера, предназначенный для синхронизации анимации с частотой обновления дисплея, обеспечивая более плавное движение по сравнению с альтернативами, такими как setTimeout. Но эффективное использование rAF требует тщательного планирования, особенно при работе с несколькими анимациями.

В этой статье мы рассмотрим, как оптимизировать requestAnimationFrame путем централизации управления анимацией, введения контроля FPS и поддержания отзывчивости основного потока браузера.


Понимание FPS и почему это важно

Количество кадров в секунду (FPS) имеет решающее значение при обсуждении производительности анимации. Большинство экранов обновляются со скоростью 60 кадров в секунду, что означает, что requestAnimationFrame вызывается 60 раз в секунду. Чтобы поддерживать плавную анимацию, браузер должен выполнять свою работу примерно за 16,67 миллисекунды на кадр.

Если в одном кадре выполняется слишком много задач, браузер может пропустить целевое время кадра, что приведет к зависанию или пропуску кадров. Понижение FPS для определенных анимаций может помочь снизить нагрузку на основной поток, обеспечивая баланс между производительностью и плавностью.

Централизованный менеджер анимации с контролем частоты кадров для повышения производительности

Чтобы управлять анимацией более эффективно, мы можем централизовать ее обработку с помощью общего цикла, а не использовать несколько вызовов requestAnimationFrame, разбросанных по всему коду. Централизованный подход сводит к минимуму избыточные вызовы и упрощает добавление контроля FPS.

Следующий класс AnimationManager позволяет нам регистрировать и отменять регистрацию задач анимации, одновременно контролируя целевой FPS. По умолчанию мы стремимся к 60 кадрам в секунду, но это можно настроить в зависимости от производительности.

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 важно помнить, что анимация выполняется в основном потоке браузера. Перегрузка основного потока слишком большим количеством задач может привести к тому, что браузер пропустит кадры анимации, что приведет к зависаниям. Вот почему оптимизация анимации с помощью таких инструментов, как централизованный менеджер анимации и контроль частоты кадров, может помочь сохранить плавность даже при использовании нескольких анимаций.


Заключение

Эффективное управление анимацией в JavaScript требует большего, чем просто использование requestAnimationFrame. Централизуя анимацию и контролируя частоту кадров, вы можете обеспечить более плавную и производительную анимацию, сохраняя при этом отзывчивость основного потока. В этом примере мы показали, как обрабатывать несколько анимаций с помощью одного 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/supergrack-your-web-animation-optimize-requestanimationframe-like-a-pro-22i5?
Последний учебник Более>

Изучайте китайский

Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.

Copyright© 2022 湘ICP备2022001581号-3