← Blog

GSAP y Framer Motion: animaciones que mejoran la UX en React sin matar el rendimiento

D
Damián Oliva
·28 de octubre de 2024

Dos herramientas distintas, dos propósitos distintos

La primera confusión que veo en desarrolladores que empiezan con animaciones en React es tratar a GSAP y Framer Motion como alternativas. No lo son exactamente: tienen casos de uso distintos y se complementan bien.

Framer Motion: animaciones de componentes React

Framer Motion está diseñada específicamente para React. Sus conceptos de variants, whileInView y AnimatePresence se integran naturalmente con el modelo mental de componentes.

Cuándo lo uso:

  • Transiciones de entrada/salida de componentes
  • Animaciones activadas por scroll con whileInView
  • Transiciones de página con AnimatePresence
  • Gestos (drag, hover, tap) en componentes interactivos
// Entrada suave con stagger
const container = {
  hidden: { opacity: 0 },
  visible: {
    opacity: 1,
    transition: { staggerChildren: 0.1 },
  },
};

const item = {
  hidden: { opacity: 0, y: 20 },
  visible: { opacity: 1, y: 0, transition: { duration: 0.5 } },
};

<motion.ul variants={container} initial="hidden" whileInView="visible" viewport={{ once: true }}>
  {items.map(i => <motion.li key={i.id} variants={item}>{i.name}</motion.li>)}
</motion.ul>

GSAP: animaciones complejas y control fino

GSAP es la herramienta más poderosa para animaciones web. Su ventaja sobre Framer Motion está en el control preciso de timelines, la capacidad de animar prácticamente cualquier propiedad CSS o SVG, y la integración con ScrollTrigger para animaciones avanzadas ligadas al scroll.

Cuándo lo uso:

  • Animaciones de entrada en páginas hero con secuencias complejas
  • Animaciones SVG o canvas
  • Efectos de parallax y pin con ScrollTrigger
  • Animaciones que necesitan timing muy preciso
import { useGSAP } from '@gsap/react';
import gsap from 'gsap';

const HeroSection = () => {
  const containerRef = useRef(null);

  useGSAP(() => {
    const tl = gsap.timeline({ delay: 0.2 });
    tl.from('.hero-title', { opacity: 0, y: 40, duration: 0.8, ease: 'power3.out' })
      .from('.hero-subtitle', { opacity: 0, y: 20, duration: 0.6 }, '-=0.4')
      .from('.hero-cta', { opacity: 0, y: 20, stagger: 0.1 }, '-=0.3');
  }, { scope: containerRef });

  return <section ref={containerRef}>...</section>;
};

Rendimiento: lo que hay que evitar

  • Animar propiedades que causan layout: evitá animar width, height, top, left. Preferí transform y opacity, que el navegador puede delegar a la GPU.
  • Animar demasiados elementos simultáneamente: usá stagger en lugar de animar todo al mismo tiempo.
  • Respetar prefers-reduced-motion: siempre agregá la media query para usuarios que prefieren sin animaciones.
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

if (!prefersReducedMotion) {
  // Tu animación acá
}
Newsletter

Ideas directamente
a tu bandeja de entrada

Comparto lo que estoy construyendo, lo que aprendo y lo que me parece interesante del mundo tech y startups. Sin spam. Cuando tenga algo que valga la pena.

Sin spam. Unsubscribe en cualquier momento.