"Se um trabalhador quiser fazer bem o seu trabalho, ele deve primeiro afiar suas ferramentas." - Confúcio, "Os Analectos de Confúcio. Lu Linggong"
Primeira página > Programação > Turbine suas animações da Web: otimize requestAnimationFrame como um profissional

Turbine suas animações da Web: otimize requestAnimationFrame como um profissional

Publicado em 2024-11-06
Navegar:116

Supercharge Your Web Animations: Optimize requestAnimationFrame Like a Pro

Animações suaves e de alto desempenho são essenciais em aplicativos da web modernos. No entanto, gerenciá-los incorretamente pode sobrecarregar o thread principal do navegador, causando baixo desempenho e animações instáveis. requestAnimationFrame (rAF) é uma API de navegador projetada para sincronizar animações com a taxa de atualização da tela, garantindo um movimento mais suave em comparação com alternativas como setTimeout. Mas o uso eficiente do rAF requer um planejamento cuidadoso, especialmente ao lidar com múltiplas animações.

Neste artigo, exploraremos como otimizar requestAnimationFrame centralizando o gerenciamento de animação, introduzindo o controle de FPS e mantendo o thread principal do navegador responsivo.


Compreendendo o FPS e por que é importante

Quadros por segundo (FPS) são cruciais ao discutir o desempenho da animação. A maioria das telas é atualizada a 60 FPS, o que significa que requestAnimationFrame é chamado 60 vezes por segundo. Para manter animações suaves, o navegador deve concluir seu trabalho em cerca de 16,67 milissegundos por quadro.

Se muitas tarefas forem executadas durante um único quadro, o navegador poderá perder o tempo de quadro alvo, causando interrupções ou queda de quadros. Reduzir o FPS para certas animações pode ajudar a reduzir a carga no thread principal, proporcionando um equilíbrio entre desempenho e suavidade.

Gerenciador de animação centralizado com controle FPS para melhor desempenho

Para gerenciar animações com mais eficiência, podemos centralizar seu tratamento com um loop compartilhado em vez de ter várias chamadas requestAnimationFrame espalhadas por todo o código. Uma abordagem centralizada minimiza chamadas redundantes e facilita a adição de controle de FPS.

A classe AnimationManager a seguir nos permite registrar e cancelar o registro de tarefas de animação enquanto controlamos o FPS alvo. Por padrão, nosso objetivo é 60 FPS, mas isso pode ser ajustado de acordo com as necessidades de desempenho.

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

Nesta configuração, calculamos o deltaTime entre os frames para determinar se passou tempo suficiente para a próxima atualização com base no FPS alvo. Isso nos permite limitar a frequência das atualizações para garantir que o thread principal do navegador não fique sobrecarregado.


Exemplo prático: animando vários elementos com propriedades diferentes

Vamos criar um exemplo onde animamos três caixas, cada uma com uma animação diferente: uma escala, outra muda de cor e a terceira gira.

Aqui está o HTML:

Aqui está o CSS:

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

Agora, adicionaremos JavaScript para animar cada caixa com uma propriedade diferente. Um será dimensionado, outro mudará de cor e o terceiro girará.

Etapa 1: Adicionar interpolação linear (lerp)

Interpolação linear (lerp) é uma técnica comum usada em animações para fazer uma transição suave entre dois valores. Ajuda a criar uma progressão gradual e suave, tornando-o ideal para dimensionar, mover ou alterar propriedades ao longo do tempo. A função leva três parâmetros: um valor inicial, um valor final e um tempo normalizado (t), que determina o quão longe está a transição.

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

Etapa 2: dimensionamento da animação

Começamos criando uma função para animar o dimensionamento da primeira caixa:

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

Etapa 3: Animação colorida

A seguir, animamos a mudança de cor da segunda caixa:

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

Etapa 4: animação de rotação

Finalmente, criamos a função para girar a terceira caixa:

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

Etapa 5: Iniciando as animações

Finalmente, podemos iniciar as animações para todas as três caixas:

// 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 o tópico principal

Ao usar requestAnimationFrame, é essencial ter em mente que as animações são executadas no thread principal do navegador. Sobrecarregar o thread principal com muitas tarefas pode fazer com que o navegador perca quadros de animação, resultando em travamentos. É por isso que otimizar suas animações com ferramentas como um gerenciador de animação centralizado e controle de FPS pode ajudar a manter a suavidade, mesmo com múltiplas animações.


Conclusão

Gerenciar animações com eficiência em JavaScript requer mais do que apenas usar requestAnimationFrame. Ao centralizar as animações e controlar o FPS, você pode garantir animações mais suaves e com melhor desempenho, mantendo a capacidade de resposta do thread principal. Neste exemplo, mostramos como lidar com múltiplas animações com um único AnimationManager, demonstrando como otimizar o desempenho e a usabilidade. Embora tenhamos nos concentrado em manter um FPS consistente para simplificar, essa abordagem pode ser expandida para lidar com diferentes valores de FPS para diversas animações, embora isso esteja além do escopo deste artigo.

Repositório GitHub: https://github.com/JBassx/rAF-optimization
StackBlitz: https://stackblitz.com/~/github.com/JBassx/rAF-optimization

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

Declaração de lançamento Este artigo foi reproduzido em: https://dev.to/josephciullo/supercharge-your-web-animations-optimize-requestanimationframe-like-a-pro-22i5?1 Se houver alguma violação, entre em contato com [email protected] para excluí-lo
Tutorial mais recente Mais>

Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.

Copyright© 2022 湘ICP备2022001581号-3