← Volver al diario

5 animaciones web que no arruinan el rendimiento (con GSAP)

Cinco animaciones concretas con GSAP que no añaden ningún coste de rendimiento perceptible. Código listo para usar, las propiedades que animar y las que evitar.

Una animación mal construida sale cara: recalcula la disposición de la página en cada fotograma, satura el hilo principal y hunde la puntuación de rendimiento que costó horas conseguir. El problema casi nunca es la animación en sí, sino la propiedad CSS que se elige animar.

Aquí tienes cinco animaciones GSAP habituales, con su código, que se mantienen fluidas a 60 fotogramas por segundo, incluso en un móvil de gama de entrada.

El principio que hay que conocer antes que todo lo demás

El navegador procesa una página en tres etapas: layout (cálculo de posiciones y tamaños), paint (dibujo de los píxeles), composite (ensamblado de las capas). Animar width, height, top, left o box-shadow desencadena layout y paint en cada fotograma, lo que pesa directamente sobre el hilo principal.

Animar transform (translate, scale, rotate) y opacity solo desencadena el composite, una etapa que el navegador delega a la GPU. Esa es la única regla que realmente importa: todas las animaciones siguientes solo usan estas dos propiedades.

1. Aparición y desplazamiento al hacer scroll

La animación más habitual, y la más veces mal hecha. Se aplica sobre translateY y opacity, nunca sobre top o margin.

import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';

gsap.registerPlugin(ScrollTrigger);

gsap.utils.toArray('.reveal').forEach((element) => {
  gsap.from(element, {
    y: 40,
    opacity: 0,
    duration: 0.6,
    ease: 'power2.out',
    scrollTrigger: {
      trigger: element,
      start: 'top 85%',
      once: true, // la animación no se repite al volver a subir
    },
  });
});

El once: true evita recalcular la animación cada vez que el scroll pasa por ahí, un detalle que importa en una página con muchas secciones.

2. Aparición en cascada sobre una lista de tarjetas

Para revelar varios elementos a la vez (servicios, proyectos, artículos), un stagger da un efecto de lectura natural sin multiplicar las instancias de GSAP.

gsap.from('.card', {
  y: 24,
  opacity: 0,
  duration: 0.5,
  stagger: 0.08, // desfase de 80ms entre cada tarjeta
  ease: 'power1.out',
  scrollTrigger: {
    trigger: '.card-grid',
    start: 'top 80%',
    once: true,
  },
});

Una sola llamada a GSAP anima toda la cuadrícula. Es más ligero que un bucle con un ScrollTrigger por tarjeta, y el resultado visual es el mismo.

3. Un efecto de hover ligero sobre un botón

Un efecto de hover que agranda o desplaza ligeramente un botón da una sensación de reactividad, siempre que se quede en scale y y.

document.querySelectorAll('.btn').forEach((button) => {
  const tween = gsap.to(button, {
    scale: 1.04,
    y: -2,
    duration: 0.2,
    ease: 'power2.out',
    paused: true,
  });

  button.addEventListener('mouseenter', () => tween.play());
  button.addEventListener('mouseleave', () => tween.reverse());
});

Crear el tween una sola vez, en estado paused, evita instanciar uno nuevo en cada hover. Eso es lo que mantiene la interacción instantánea, incluso en una página con muchos botones.

4. Un parallax suave sobre una imagen

El parallax suele construirse con background-position, una propiedad que desencadena paint en cada fotograma de scroll. La versión con buen rendimiento desplaza un elemento mediante transform, con scrub para sincronizar la animación con el scroll.

gsap.to('.parallax-image', {
  yPercent: 15, // desplazamiento relativo, sin recálculo de layout
  ease: 'none',
  scrollTrigger: {
    trigger: '.parallax-section',
    start: 'top bottom',
    end: 'bottom top',
    scrub: 0.5, // sigue el scroll con un ligero retardo
  },
});

yPercent se apoya internamente en transform: translateY(). La imagen debe ser un poco más grande que su contenedor (overflow: hidden en el padre) para que el desplazamiento no deje un borde visible.

5. Un contador animado para cifras clave

Animar un número mostrado (“120 proyectos”, “98% de satisfacción”) no toca ninguna propiedad CSS: GSAP anima un valor de JavaScript, y el resultado se escribe en el texto en cada fotograma.

document.querySelectorAll('.counter').forEach((element) => {
  const target = Number(element.dataset.target);
  const counter = { value: 0 };

  gsap.to(counter, {
    value: target,
    duration: 1.2,
    ease: 'power1.out',
    scrollTrigger: {
      trigger: element,
      start: 'top 90%',
      once: true,
    },
    onUpdate: () => {
      element.textContent = Math.round(counter.value);
    },
  });
});

El coste real de esta animación es el textContent reescrito en cada fotograma, algo que sigue siendo insignificante para un solo número. Eso sí, hay que evitar aplicarlo a un bloque de texto grande: ahí, el coste de actualizar el DOM vuelve a notarse.

El hábito que hay que conservar

Antes de añadir una animación, basta una pregunta: ¿toca únicamente transform y opacity? Si la respuesta es no, casi siempre existe una variante que sí lo hace. El segundo hábito es medir: la pestaña Performance de las DevTools muestra en segundos si una animación desencadena layout, y Lighthouse penaliza directamente una puntuación de rendimiento degradada, un tema que detallo en mi artículo sobre los Core Web Vitals.

Es la misma disciplina que aplico en mis propios proyectos, descrita con más detalle en por qué elegí Astro y GSAP para mi portfolio: animaciones que añaden fluidez, nunca un lastre de rendimiento.

Hablemos de su proyecto

¿Quiere animaciones cuidadas en su sitio sin sacrificar la velocidad de carga? Cuénteme su proyecto: le muestro lo que es posible dentro de una creación de sitio web pensada para seguir siendo rápida.