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.
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.
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.
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.
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>
);
}
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 Gesto | Descripción | Ejemplo de Uso | कठिनाई |
|---|---|---|---|
whileHover | Aplica estilos cuando el cursor pasa por encima | whileHover={{ scale: 1.1 }} | Fácil |
whileTap | Aplica estilos al hacer clic o pulsar el elemento | whileTap={{ scale: 0.95 }} | Fácil |
drag | Permite arrastrar el elemento libremente en la pantalla | drag dragConstraints={{ left: 0, right: 300 }} | Intermedio |
whileDrag | Estilos aplicados específicamente mientras se arrastra | whileDrag={{ 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>
);
}
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 propclassName 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
- React Server Components y Suspense: Renderizado Híbrido y Experiencias de Usuario Avanzadas 🚀advanced20 min
- React Testing Library: Desentrañando Pruebas Robustas y Centradas en el Usuario 🧪intermediate25 min
- React Concurrent Mode y Suspense: Renderizado No Bloqueante y Carga de Datos Avanzada 🚀advanced25 min
- Optimización del Renderizado en React con `useMemo`, `useCallback` y `React.memo` 🚀intermediate15 min
- Patrones de Diseño de Componentes Compuestos en React: Arquitectura Avanzada y Flexibleadvanced12 min
Comentarios (0)
Aún no hay comentarios. ¡Sé el primero!