"Si un trabajador quiere hacer bien su trabajo, primero debe afilar sus herramientas." - Confucio, "Las Analectas de Confucio. Lu Linggong"
Página delantera > Programación > Potencia tus animaciones web: optimiza requestAnimationFrame como un profesional

Potencia tus animaciones web: optimiza requestAnimationFrame como un profesional

Publicado el 2024-11-06
Navegar:604

Supercharge Your Web Animations: Optimize requestAnimationFrame Like a Pro

Las animaciones fluidas y eficaces son esenciales en las aplicaciones web modernas. Sin embargo, administrarlos incorrectamente puede sobrecargar el hilo principal del navegador, provocando un rendimiento deficiente y animaciones deficientes. requestAnimationFrame (rAF) es una API del navegador diseñada para sincronizar animaciones con la frecuencia de actualización de la pantalla, lo que garantiza un movimiento más fluido en comparación con alternativas como setTimeout. Pero usar rAF de manera eficiente requiere una planificación cuidadosa, especialmente cuando se manejan múltiples animaciones.

En este artículo, exploraremos cómo optimizar requestAnimationFrame centralizando la administración de animaciones, introduciendo el control de FPS y manteniendo la capacidad de respuesta del hilo principal del navegador.


Comprender los FPS y por qué son importantes

Los cuadros por segundo (FPS) son cruciales cuando se habla del rendimiento de la animación. La mayoría de las pantallas se actualizan a 60 FPS, lo que significa que se llama a requestAnimationFrame 60 veces por segundo. Para mantener animaciones fluidas, el navegador debe completar su trabajo en aproximadamente 16,67 milisegundos por cuadro.

Si se ejecutan demasiadas tareas durante un solo fotograma, el navegador podría perder el tiempo del fotograma objetivo, lo que provocaría tartamudeo o pérdida de fotogramas. Reducir el FPS para ciertas animaciones puede ayudar a reducir la carga en el hilo principal, proporcionando un equilibrio entre rendimiento y fluidez.

Administrador de animación centralizado con control FPS para un mejor rendimiento

Para administrar las animaciones de manera más eficiente, podemos centralizar su manejo con un bucle compartido en lugar de tener múltiples llamadas requestAnimationFrame dispersas por todo el código. Un enfoque centralizado minimiza las llamadas redundantes y facilita agregar control de FPS.

La siguiente clase AnimationManager nos permite registrar y cancelar el registro de tareas de animación mientras controlamos el FPS objetivo. De forma predeterminada, nuestro objetivo es 60 FPS, pero esto se puede ajustar según las necesidades de rendimiento.

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();

En esta configuración, calculamos el deltaTime entre cuadros para determinar si ha pasado suficiente tiempo para la siguiente actualización según el FPS objetivo. Esto nos permite limitar la frecuencia de las actualizaciones para garantizar que el hilo principal del navegador no esté sobrecargado.


Ejemplo práctico: animar varios elementos con diferentes propiedades

Creemos un ejemplo en el que animamos tres cuadros, cada uno con una animación diferente: uno escala, otro cambia de color y el tercero gira.

Aquí está el HTML:

Aquí está el CSS:

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

Ahora agregaremos JavaScript para animar cada cuadro con una propiedad diferente. Uno escalará, otro cambiará de color y el tercero rotará.

Paso 1: Agregar interpolación lineal (lerp)

La interpolación lineal (lerp) es una técnica común utilizada en animaciones para realizar una transición suave entre dos valores. Ayuda a crear una progresión gradual y suave, lo que lo hace ideal para escalar, mover o cambiar propiedades con el tiempo. La función toma tres parámetros: un valor inicial, un valor final y un tiempo normalizado (t), que determina qué tan avanzada está la transición.

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

Paso 2: Escalar la animación

Comenzamos creando una función para animar la escala del primer cuadro:

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);
}

Paso 3: Animación en color

A continuación, animamos el cambio de color del segundo cuadro:

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);
}

Paso 4: Animación de rotación

Finalmente, creamos la función para rotar el tercer cuadro:

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);
}

Paso 5: Iniciar las animaciones

Finalmente, podemos iniciar las animaciones de los tres cuadros:

// 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

Nota sobre el hilo principal

Al utilizar requestAnimationFrame, es esencial tener en cuenta que las animaciones se ejecutan en el hilo principal del navegador. Sobrecargar el hilo principal con demasiadas tareas puede hacer que el navegador pierda fotogramas de animación, lo que provoca tartamudeo. Es por eso que optimizar tus animaciones con herramientas como un administrador de animación centralizado y control de FPS puede ayudar a mantener la fluidez, incluso con múltiples animaciones.


Conclusión

Administrar animaciones de manera eficiente en JavaScript requiere algo más que simplemente usar requestAnimationFrame. Al centralizar las animaciones y controlar el FPS, puede garantizar animaciones más fluidas y con mayor rendimiento mientras mantiene la capacidad de respuesta del hilo principal. En este ejemplo, mostramos cómo manejar múltiples animaciones con un solo AnimationManager, demostrando cómo optimizar tanto el rendimiento como la usabilidad. Si bien nos concentramos en mantener un FPS constante para simplificar, este enfoque se puede ampliar para manejar diferentes valores de FPS para varias animaciones, aunque eso estaba más allá del alcance de este artículo.

Repositorio de Github: https://github.com/JBassx/rAF-optimization
StackBlitz: https://stackblitz.com/~/github.com/JBassx/rAF-optimization

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

Declaración de liberación Este artículo se reproduce en: https://dev.to/josephciullo/supercharge-your-web-animations-optimize-requestanimationframe-like-a-pro-22i5?1 Si hay alguna infracción, comuníquese con [email protected] para borrarlo
Último tutorial Más>

Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.

Copyright© 2022 湘ICP备2022001581号-3