tutoriales.com

Manejo de Animaciones Avanzadas en React con Framer Motion 🚀

Este tutorial práctico y completo te guiará desde los conceptos básicos de animación en React hasta la creación de complejas transiciones basadas en gestos y orquestación de componentes usando Framer Motion.

Intermedio8 min de lectura5 views
Reportar error

Introducción a las Animaciones en React con Framer Motion

Crear interfaces de usuario atractivas y fluidas es hoy en día un estándar en el desarrollo web moderno. Aunque CSS ofrece excelentes capacidades de transición, cuando se trata de aplicaciones basadas en componentes como React, la gestión de estados de animación complejos, gestos de usuario avanzados y transiciones de salida (mount/unmount) puede volverse un desafío considerable. Aquí es donde entra en juego Framer Motion, una biblioteca de producción para React que simplifica enormemente la creación de animaciones fluidas y declarativas.

En este tutorial exhaustivo, exploraremos cómo integrar Framer Motion en tus proyectos de React para llevar la experiencia de usuario al siguiente nivel. Aprenderás a animar elementos basados en estados, gestionar gestos como arrastrar y pulsar, y orquestar secuencias complejas con múltiples componentes.

💡 Consejo: Framer Motion transforma componentes HTML y SVG estándar en elementos animados añadiendo el prefijo motion, por ejemplo: motion.div o motion.button.

Configuración e Instalación del Entorno

Para comenzar a trabajar con Framer Motion, primero necesitamos instalar la librería en un proyecto de React existente. Asegúrate de tener Node.js instalado en tu sistema y ejecuta el siguiente comando en tu terminal dentro de la carpeta del proyecto:

npm install framer-motion

o si prefieres utilizar Yarn:

yarn add framer-motion

Una vez instalada la dependencia, podemos verificar que todo funcione correctamente creando un componente básico de prueba. A continuación, veremos cómo estructurar nuestro primer componente animado.


Tu Primer Componente Animado: Propiedades animate e initial

El núcleo de Framer Motion reside en la propiedad animate. A diferencia del CSS tradicional donde definimos fotogramas clave (keyframes), en Framer Motion simplemente declaramos el estado final al que queremos que llegue el componente.

Estado Inicial initial={{ opacity: 0, scale: 0.8 }} Transición Estado Animado animate={{ opacity: 1, scale: 1 }} Framer Motion Lifecycle

Vamos a crear un componente funcional llamado BoxAnimator.jsx que demuestre una animación simple de aparición, cambio de escala y rotación:

import React from 'react';
import { motion } from 'framer-motion';

export default function BoxAnimator() {
  return (
    <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
      <motion.div
        initial={{ opacity: 0, scale: 0.8, rotate: 0 }}
        animate={{ opacity: 1, scale: 1, rotate: 360 }}
        transition={{ duration: 1.2, ease: "easeInOut" }}
        style={{
          width: 150,
          height: 150,
          backgroundColor: '#4A90D9',
          borderRadius: '16px',
          boxShadow: '0 10px 25px rgba(0,0,0,0.2)'
        }}
      />
    </div>
  );
}

Explicación de los Parámetros Principales

  • initial: Define el estado visual del componente antes de que sea montado en el DOM.
  • animate: Establece el estado objetivo hacia el cual el componente se animará de inmediato tras el montaje.
  • transition: Controla la física de la animación, incluyendo la duración, el tipo de curva de aceleración (ease, spring, etc.) y los retrasos.
📌 Nota: Las transiciones de tipo muelle (spring) están activadas por defecto si no especificas una función de aceleración, lo que otorga una sensación natural y orgánica a los movimientos.

Variantes: Organizando Animaciones Complejas

A medida que nuestras aplicaciones crecen, mantener las propiedades de animación directamente en línea dentro de los componentes puede desorganizar el código. Framer Motion resuelve esto mediante el concepto de Variants (Variantes).

Las variantes permiten agrupar los estados de animación en un objeto separado y reutilizable, facilitando también la propagación automática de animaciones a los componentes hijos.

Creando Variantes Reutilizables

Veamos un ejemplo de un menú desplegable animado utilizando variantes:

import React, { useState } from 'react';
import { motion } from 'framer-motion';

const menuVariants = {
  closed: {
    opacity: 0,
    y: -20,
    transition: {
      duration: 0.2
    }
  },
  open: {
    opacity: 1,
    y: 0,
    transition: {
      duration: 0.4,
      staggerChildren: 0.1,
      delayChildren: 0.2
    }
  }
};

const itemVariants = {
  closed: { opacity: 0, x: -10 },
  open: { opacity: 1, x: 0 }
};

export default function DropdownMenu() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <div style={{ position: 'relative', width: 200, fontFamily: 'sans-serif' }}>
      <button
        onClick={() => setIsOpen(!isOpen)}
        style={{ padding: '10px 20px', cursor: 'pointer', width: '100%' }}
      >
        {isOpen ? 'Cerrar Menú' : 'Abrir Menú'}
      </button>
      <motion.ul
        initial="closed"
        animate={isOpen ? "open" : "closed"}
        variants={menuVariants}
        style={{
          listStyle: 'none',
          padding: 10,
          margin: '10px 0 0 0',
          background: '#f4f4f4',
          borderRadius: 8,
          position: 'absolute',
          width: '100%'
        }}
      >
        <motion.li variants={itemVariants} style={{ padding: '8px 0' }}>Perfil</motion.li>
        <motion.li variants={itemVariants} style={{ padding: '8px 0' }}>Configuración</motion.li>
        <motion.li variants={itemVariants} style={{ padding: '8px 0' }}>Cerrar Sesión</motion.li>
      </motion.ul>
    </div>
  );
}
🔥 Importante: Gracias a propiedades como staggerChildren, podemos lograr que los elementos hijos se animen uno tras otro con un retraso escalonado automático, generando un efecto visual muy pulido.

Gestos Interactivos: Drag, Hover y Tap

Framer Motion destaca no solo por animar estados, sino por hacer que las interacciones del usuario respondan de forma fluida a través de props dedicadas a gestos.

Tabla Comparativa de Gestos en Framer Motion

Propiedad de GestoDescripciónEjemplo de Usoकठिनाई
whileHoverAplica estilos cuando el cursor pasa por encimawhileHover={{ scale: 1.1 }}Fácil
whileTapAplica estilos al hacer clic o pulsar el elementowhileTap={{ scale: 0.95 }}Fácil
dragPermite arrastrar el elemento libremente en la pantalladrag dragConstraints={{ left: 0, right: 300 }}Intermedio
whileDragEstilos aplicados específicamente mientras se arrastrawhileDrag={{ scale: 1.05, cursor: 'grabbing' }}Intermedio

Implementemos un botón interactivo y una tarjeta arrastrable para ver esto en acción:

import React from 'react';
import { motion } from 'framer-motion';

export default function InteractiveCard() {
  return (
    <div style={{ display: 'flex', gap: '20px', padding: '50px' }}>
      {/* Botón con hover y tap */}
      <motion.button
        whileHover={{ scale: 1.1, backgroundColor: '#357abd' }}
        whileTap={{ scale: 0.95 }}
        style={{
          padding: '12px 24px',
          backgroundColor: '#4A90D9',
          color: '#fff',
          border: 'none',
          borderRadius: '8px',
          fontSize: '16px',
          cursor: 'pointer'
        }}
      >
        ¡Púlsame!
      </motion.button>

      {/* Tarjeta arrastrable */}
      <motion.div
        drag
        dragConstraints={{ top: -50, left: -50, right: 200, bottom: 200 }}
        whileHover={{ cursor: 'grab' }}
        whileDrag={{ cursor: 'grabbing', scale: 1.05 }}
        style={{
          width: 180,
          height: 120,
          backgroundColor: '#ff6b6b',
          borderRadius: '12px',
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          color: '#fff',
          fontWeight: 'bold'
        }}
      >
        ¡Arrástrame!
      </motion.div>
    </div>
  );
}

Manejo de Salidas y Desmontaje con AnimatePresence

Por defecto, React elimina los componentes del DOM de manera instantánea cuando cambian de estado (por ejemplo, al cerrar un modal o eliminar un elemento de una lista), lo que impide que las animaciones de salida (exit animations) se ejecuten.

Para solucionar esto, Framer Motion provee el componente AnimatePresence.

Ejemplo de Lista Animada con Elementos Eliminables

import React, { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';

export default function AnimatedList() {
  const [items, setItems] = useState(['Elemento 1', 'Elemento 2', 'Elemento 3']);

  const removeItem = (itemToRemove) => {
    setItems(items.filter(item => item !== itemToRemove));
  };

  return (
    <div style={{ width: 300, margin: '20px auto', fontFamily: 'sans-serif' }}>
      <h3>Lista con AnimatePresence</h3>
      <ul style={{ listStyle: 'none', padding: 0 }}>
        <AnimatePresence>
          {items.map((item) => (
            <motion.li
              key={item}
              initial={{ opacity: 0, x: -50 }}
              animate={{ opacity: 1, x: 0 }}
              exit={{ opacity: 0, x: 50 }}
              transition={{ duration: 0.3 }}
              style={{
                padding: '12px',
                margin: '8px 0',
                backgroundColor: '#e3f2fd',
                borderRadius: '6px',
                display: 'flex',
                justifyContent: 'space-between',
                alignItems: 'center'
              }}
            >
              {item}
              <button
                onClick={() => removeItem(item)}
                style={{
                  background: '#f44336',
                  color: '#fff',
                  border: 'none',
                  padding: '4px 8px',
                  borderRadius: '4px',
                  cursor: 'pointer'
                }}
              >
                X
              </button>
            </motion.li>
          ))}
        </AnimatePresence>
      </ul>
    </div>
  );
}
⚠️ Advertencia: Para que AnimatePresence funcione correctamente al mapear elementos de una lista, es obligatorio que cada elemento hijo posea una prop key única y estable.

Preguntas Frecuentes sobre Framer Motion

¿Framer Motion afecta negativamente el rendimiento de mi aplicación React? Framer Motion está optimizado para ejecutarse principalmente en el hilo de animación del navegador mediante transformaciones CSS y aceleración por GPU, lo que garantiza un rendimiento fluido a 60fps incluso en dispositivos móviles.
¿Puedo usar Framer Motion con componentes estilizados de Styled Components o Tailwind CSS? Sí, perfectamente. Puedes pasar clases de Tailwind mediante la prop className o envolver tus componentes de Styled Components utilizando motion(StyledComponent).

Conclusión y Próximos Pasos

Framer Motion es una herramienta indispensable para desarrolladores de React que buscan crear interfaces modernas, interactivas y visualmente impactantes sin la complejidad de escribir animaciones CSS puras o manipular el DOM manualmente.

Te animamos a experimentar combinando variantes, gestos avanzados y AnimatePresence en tus propios proyectos para dominar por completo esta poderosa librería.

Tutoriales relacionados

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!