W3docs

Animações em JavaScript

Aprenda animações em JavaScript com requestAnimationFrame, easing, encadeamento, Web Animations API e boas práticas de desempenho e acessibilidade.

As animações em JavaScript são uma forma dinâmica de enriquecer a experiência do usuário nas páginas web. De efeitos sutis a movimentos complexos, dominar as animações em JavaScript pode elevar significativamente a interatividade e o apelo visual dos seus projetos. Este guia completo apresenta uma exploração aprofundada das animações em JavaScript, incluindo conceitos fundamentais, técnicas essenciais e exemplos práticos para ajudar você a criar animações envolventes com proficiência.

Introdução às animações em JavaScript

As animações em JavaScript manipulam elementos do Document Object Model (DOM) para criar efeitos visuais. Elas são usadas para mover, redimensionar e alterar elementos em resposta às interações do usuário ou automaticamente ao longo do tempo. Ao compreender os princípios e as técnicas centrais, você pode criar animações que sejam, ao mesmo tempo, performáticas e visualmente atraentes.

Por que usar JavaScript para animações?

As animações em JavaScript oferecem mais controle e flexibilidade do que as animações CSS. Elas permitem sequências e interações mais complexas, tornando possível criar efeitos altamente personalizados. As animações em JavaScript podem ser controladas dinamicamente e costumam ser necessárias quando você precisa responder a entradas do usuário ou integrar-se a outras funcionalidades de JavaScript.

Como regra geral:

  • Use CSS (transition/@keyframes) para mudanças de estado simples e declarativas, como hovers, fades e alternâncias (toggles). O navegador otimiza esses recursos em uma thread de composição (compositor) separada, por isso costumam ser a escolha mais performática. Veja animações CSS.
  • Use JavaScript quando a animação depender de dados, gestos do usuário, física ou lógica que o CSS não consegue expressar — por exemplo, seguir o mouse, encaixar em posições dinâmicas ou encadear muitas etapas com condições de ramificação.

Para a opção em JavaScript que exige menos esforço, a Web Animations API nativa (abordada mais adiante) oferece desempenho no nível do CSS com controle imperativo, enquanto requestAnimationFrame oferece controle total quadro a quadro quando você precisar.

Conceitos básicos das animações em JavaScript

Antes de mergulhar em técnicas específicas, é essencial entender alguns conceitos básicos que sustentam as animações em JavaScript:

  1. Quadros por segundo (FPS): a taxa na qual os quadros da animação são exibidos. Um FPS mais alto resulta em animações mais suaves.
  2. Funções de easing: essas funções controlam a aceleração e a desaceleração das animações, adicionando um movimento natural.
  3. Funções de temporização: determinam a duração e o atraso das animações.
  4. Keyframes: definem os estados inicial, final e intermediários de uma animação.

requestAnimationFrame vs. setInterval

Antes de requestAnimationFrame existir, as animações eram conduzidas por setInterval e setTimeout, executando um callback a cada poucos milissegundos. Essa abordagem tem dois problemas: o intervalo não é sincronizado com a taxa de atualização da tela (causando quadros perdidos ou duplicados) e o temporizador continua disparando mesmo quando a aba está em segundo plano, desperdiçando CPU e bateria.

requestAnimationFrame(callback) resolve os dois problemas. O navegador chama o seu callback imediatamente antes da próxima repintura — normalmente cerca de 60 vezes por segundo, acompanhando o monitor — e pausa automaticamente quando a aba está oculta. O callback recebe um argumento timestamp de alta resolução, que você usa para calcular o progresso com base no tempo real decorrido, em vez de presumir uma taxa de quadros fixa.

// setInterval: fixed timer, not synced to the screen, runs in background tabs
let pos = 0;
const id = setInterval(() => {
  pos += 2;
  if (pos >= 200) clearInterval(id);
}, 16);

// requestAnimationFrame: synced to repaint, pauses in background tabs
function frame(timestamp) {
  // use timestamp to drive the animation
  requestAnimationFrame(frame);
}
requestAnimationFrame(frame);

Use requestAnimationFrame para tudo o que atualiza o visual a cada quadro.

Exemplo simples de animação em JavaScript

Vamos começar com um exemplo simples em que animamos um quadrado se movendo pela tela.

<!DOCTYPE html>
<html>
<head>
  <style>
    #square {
      width: 50px;
      height: 50px;
      background-color: red;
      position: absolute;
      left: 0;
      top: 50px;
    }
  </style>
</head>
<body>
  <div id="square"></div>
  <script>
    const square = document.getElementById('square');
    let pos = 0;

    function move() {
      if (pos < window.innerWidth - 50) {
        pos += 2;
        square.style.transform = 'translateX(' + pos + 'px)';
        requestAnimationFrame(move);
      }
    }

    move();
  </script>
</body>
</html>

Este exemplo demonstra uma animação básica em que um quadrado vermelho se move do lado esquerdo da tela para o direito. A animação usa a função requestAnimationFrame, que garante um movimento suave ao sincronizar a animação com a taxa de atualização do monitor. A função move atualiza a posição do quadrado de forma incremental e chama a si mesma continuamente até que o quadrado alcance a borda da janela.

Informação

Embora as animações em JS exijam atualizações frequentes de estilo, prefira transform e opacity para evitar disparar reflows de layout.

Técnicas avançadas em animações JavaScript

Usando funções de easing

As funções de easing tornam as animações mais naturais ao variar a velocidade da animação. Elas normalmente esperam um valor de progresso normalizado entre 0 e 1. Veja um exemplo que usa uma função de easing para animar o quadrado:

<!DOCTYPE html>
<html>
<head>
  <style>
    #square {
      width: 50px;
      height: 50px;
      background-color: blue;
      position: absolute;
      left: 0;
      top: 50px;
    }
  </style>
</head>
<body>
  <div id="square"></div>
  <script>
    const square = document.getElementById('square');
    let start = null;
    const duration = 2000; // Animation duration in milliseconds

    function easeOutQuad(t) {
      return t * (2 - t);
    }

    function animate(timestamp) {
      if (!start) start = timestamp;
      let elapsed = timestamp - start;
      let progress = Math.min(elapsed / duration, 1);
      let easedProgress = easeOutQuad(progress);

      square.style.transform = 'translateX(' + (window.innerWidth - 50) * easedProgress + 'px)';

      if (progress < 1) {
        requestAnimationFrame(animate);
      }
    }

    requestAnimationFrame(animate);
  </script>
</body>
</html>

Este exemplo aprimora a animação simples incorporando uma função de easing chamada easeOutQuad. As funções de easing criam animações com aparência mais natural ao ajustar a velocidade do elemento animado ao longo do tempo. Neste caso, o quadrado azul se move da esquerda para a direita, começando rápido e desacelerando conforme se aproxima do fim. A duração da animação é definida em 2000 milissegundos, e a posição do quadrado é atualizada com base no progresso suavizado pelo easing.

Encadeando animações

Encadear animações permite executar várias animações em sequência. Veja um exemplo de um quadrado que primeiro se move para a direita e depois para baixo:

<!DOCTYPE html>
<html>
<head>
  <style>
    #square {
      width: 50px;
      height: 50px;
      background-color: green;
      position: absolute;
      left: 0;
      top: 0;
    }
  </style>
</head>
<body>
  <div id="square"></div>
  <script>
    const square = document.getElementById('square');
    let xPos = 0;
    let yPos = 0;

    function moveRight() {
      if (xPos < window.innerWidth - 50) {
        xPos += 2;
        square.style.transform = 'translateX(' + xPos + 'px)';
        requestAnimationFrame(moveRight);
      } else {
        xPos = 0;
        yPos = 0; // Explicitly reset yPos before downward phase
        square.style.transform = 'translateX(0px)';
        requestAnimationFrame(moveDown);
      }
    }

    function moveDown() {
      if (yPos < window.innerHeight - 50) {
        yPos += 2;
        square.style.transform = 'translateY(' + yPos + 'px)';
        requestAnimationFrame(moveDown);
      } else {
        yPos = 0; // Reset for repeated cycles
        requestAnimationFrame(moveRight);
      }
    }

    moveRight();
  </script>
</body>
</html>

Este exemplo demonstra o encadeamento de animações ao mover um quadrado verde primeiro para a direita e depois para baixo. A função moveRight move o quadrado para a direita até que ele alcance a borda da janela. Ao chegar à borda direita, yPos é explicitamente redefinido para 0 antes de moveDown ser chamada. Essa execução sequencial, combinada com um gerenciamento de estado adequado para ciclos repetidos, mostra como criar sequências de animação mais complexas encadeando várias animações.

Animando várias propriedades

Animar várias propriedades simultaneamente pode criar efeitos mais complexos e visualmente atraentes. Veja um exemplo que altera tanto a posição quanto a cor:

<!DOCTYPE html>
<html>
<head>
  <style>
    #square {
      width: 50px;
      height: 50px;
      background-color: purple;
      position: absolute;
      left: 0;
      top: 0;
    }
  </style>
</head>
<body>
  <div id="square"></div>
  <script>
    const square = document.getElementById('square');
    let pos = 0;

    function changeColor(progress) {
      let red = Math.floor(255 * progress);
      let green = Math.floor(255 * (1 - progress));
      return `rgb(${red}, ${green}, 0)`;
    }

    function animate() {
      if (pos < window.innerWidth - 50) {
        pos += 2;
        let progress = pos / (window.innerWidth - 50);
        square.style.transform = 'translateX(' + pos + 'px)';
        square.style.backgroundColor = changeColor(progress);
        requestAnimationFrame(animate);
      }
    }

    animate();
  </script>
</body>
</html>

Este exemplo anima tanto a posição quanto a cor de fundo de um quadrado roxo. Conforme o quadrado se move da esquerda para a direita, sua cor passa de vermelho para verde. A função changeColor calcula a cor com base no progresso da animação, criando um efeito de gradiente. Isso demonstra como animar várias propriedades simultaneamente, acrescentando complexidade e interesse visual às animações.

A Web Animations API

Escrever manualmente um loop com requestAnimationFrame oferece controle total, mas, para a maioria das animações baseadas em linha do tempo, o navegador já tem um mecanismo nativo: a Web Animations API (WAAPI). Chamar element.animate(keyframes, options) executa a animação fora da thread principal sempre que possível (assim como no CSS), de modo que você obtém uma reprodução suave sem precisar escrever um loop de quadros. Ela também retorna um object Animation que você pode pausar, reverter, reposicionar ou aguardar.

<!DOCTYPE html>
<html>
<head>
  <style>
    #box {
      width: 50px;
      height: 50px;
      background-color: teal;
      position: absolute;
      left: 0;
      top: 50px;
    }
  </style>
</head>
<body>
  <div id="box"></div>
  <script>
    const box = document.getElementById('box');

    const animation = box.animate(
      [
        { transform: 'translateX(0)', opacity: 1 },
        { transform: 'translateX(300px)', opacity: 0.3 }
      ],
      {
        duration: 2000,
        easing: 'ease-out',
        fill: 'forwards'
      }
    );

    // The returned object gives you imperative control:
    animation.finished.then(() => console.log('done'));
    // animation.pause();  animation.reverse();  animation.currentTime = 1000;
  </script>
</body>
</html>

O primeiro argumento é um array de keyframes (cada um é um object com propriedades CSS); o segundo define duration, easing e comportamentos como fill (manter o estado final), iterations, direction e delay. Como easing aceita os mesmos valores cubic-bezier() e palavras-chave do CSS, muitas vezes você nem precisa escrever a matemática do easing manualmente.

Prefira a WAAPI quando a sua animação for uma linha do tempo fixa de mudanças de propriedades. Reserve um loop manual com requestAnimationFrame para os casos em que cada quadro depende de entrada em tempo real ou de um estado calculado — como física, arrastar elementos ou valores que não são conhecidos de antemão.

Parando e revertendo animações

Um loop com requestAnimationFrame continua em execução até que você pare de agendar novos quadros. Para cancelá-lo imediatamente, salve o ID retornado por requestAnimationFrame e passe-o para cancelAnimationFrame:

let rafId;

function loop(timestamp) {
  // ...update visuals...
  rafId = requestAnimationFrame(loop);
}

rafId = requestAnimationFrame(loop);

// Later, e.g. on a button click or when the element is removed:
cancelAnimationFrame(rafId);

Sempre cancele as animações em execução antes de remover seus elementos do DOM; caso contrário, o loop continua modificando um nó desanexado e desperdiça processamento a cada quadro. Com a Web Animations API, os equivalentes são simplesmente animation.cancel(), animation.pause() e animation.reverse().

Acessibilidade: respeite a preferência por movimento reduzido

Movimentos grandes ou rápidos podem provocar náuseas, tontura ou enxaquecas em alguns usuários. Os sistemas operacionais expõem uma preferência de "reduzir movimento", e você deve respeitá-la. Em JavaScript, leia essa preferência com matchMedia e desative a animação ou salte diretamente para o estado final:

const prefersReducedMotion = window.matchMedia(
  '(prefers-reduced-motion: reduce)'
).matches;

if (prefersReducedMotion) {
  // Skip the animation: apply the final state instantly
  square.style.transform = 'translateX(300px)';
} else {
  requestAnimationFrame(animate);
}

Essa única verificação torna as suas interfaces mais inclusivas e mais agradáveis para os usuários que optam por não ter movimento.

Otimização de desempenho para animações em JavaScript

Para garantir animações suaves e eficientes, siga estas dicas de otimização de desempenho:

  1. Use requestAnimationFrame: este método se sincroniza com a taxa de atualização do monitor, proporcionando animações mais suaves.
  2. Minimize as manipulações do DOM: agrupe as atualizações do DOM para reduzir reflows e repinturas. Prefira transform e opacity para mudanças de posição, aproveitando a aceleração por GPU.
  3. Otimize o CSS: use propriedades CSS aceleradas por GPU, como transform e opacity.
  4. Limite a frequência dos eventos (throttle): restrinja a frequência de eventos como resize ou scroll usando técnicas de throttling ou debouncing.
  5. Pare as animações corretamente: use cancelAnimationFrame para interromper as animações quando elas não forem mais necessárias. Armazene o ID da animação e chame cancelAnimationFrame(id) para evitar vazamentos de memória. Exemplo:
    let animationId = requestAnimationFrame(animate);
    // ... later ...
    cancelAnimationFrame(animationId);
  6. Evite o layout thrashing: não leia propriedades de layout (por exemplo, offsetWidth, getBoundingClientRect) dentro do loop da animação, pois isso obriga o navegador a recalcular os estilos no meio do quadro e pode causar travamentos (jank).
Informação

Você também pode criar animações apenas com CSS (sem JavaScript), e isso geralmente resulta em melhor desempenho. Veja animações CSS.

Conclusão

Dominar as animações em JavaScript envolve compreender os princípios básicos, implementar técnicas avançadas e otimizar o desempenho. Seguindo este guia, você poderá criar animações envolventes e responsivas que melhoram a experiência do usuário. Experimente diferentes funções de easing, encadeamento e animações de propriedades para liberar todo o potencial do JavaScript nos seus projetos web.

Para ir além, explore estes tópicos relacionados:

Practice

Prática
Quais métodos e funções são comumente usados em animações JavaScript?
Quais métodos e funções são comumente usados em animações JavaScript?
Was this page helpful?