Animaciones Web: Del Movimiento al Significado
URL: https://ruvebal.github.io/web-atelier-udit/lessons/es/web-animations/
📋 Tabla de Contenidos
- ⚡ Inicio Rápido (10 minutos)
- 🎯 Objetivos de Aprendizaje
- 🎭 Perspectiva Crítica: La Animación como Lenguaje
- 📜 La Evolución de la Animación Web
- 🎓 Teoría: Los 3 Pilares de la Animación CSS
- ⚡ Rendimiento: La Optimización “Will-Change”
- ♿ Accesibilidad: Respetando Preferencias de Movimiento del Usuario
- 🏗️ Taller Atelier: Construyendo Sistemas de Animación
- 🎨 Ejercicio 1: Micro-Interacciones (Estados de Botón)
- 🎨 Ejercicio 2: Animaciones de Carga de Página (Fade-In Escalonado)
- 🎨 Ejercicio 3: Estados de Carga (Pantallas Skeleton y Spinners)
- 🎨 Ejercicio 4: Animaciones SVG (Íconos y Logos)
- 🎯 Práctica Avanzada: Formas Metamorfoseantes (CSS clip-path)
- 🎯 Práctica Avanzada: Animaciones de Texto (Typewriter, Glitch, Gradient)
- 🎯 Práctica Avanzada: Animaciones Impulsadas por Scroll (CSS Moderno)
- 🏆 Desafío Experto: View Transitions API
- 📚 Lista de Verificación de Rendimiento de Animación
- 🎓 Reflexión Atelier: Cuándo NO Animar
- 🌍 Estudios de Caso del Mundo Real
- 📝 Asignación: Anima Tu Portafolio
- 🎯 Desafíos Opcionales Avanzados
- 📚 Aprendizaje Adicional
- 🎓 Reflexión Final Atelier
- 📝 Commit Tu Aprendizaje
⚡ Inicio Rápido (10 minutos)
- Crea
styles/animations.cssy pega el snippet de abajo. - Añade el HTML exactamente como se muestra en cualquier página.
- Activa “Reducir movimiento” en el sistema operativo para verificar accesibilidad.
/* Sistema mínimo de animación para el taller */
:root {
--duration-fast: 200ms;
--duration: 300ms;
--ease-out: cubic-bezier(0, 0, 0.2, 1);
--color-primary: #3b82f6;
}
/* Respetar preferencias de movimiento */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* Micro-interacción de botón */
.btn {
padding: 0.75rem 1.5rem;
border: 0;
border-radius: 6px;
background: var(--color-primary);
color: #fff;
font-weight: 600;
transition: transform var(--duration-fast) var(--ease-out), box-shadow var(--duration-fast) var(--ease-out);
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.15);
}
.btn:active {
transform: translateY(0);
transition-duration: 100ms;
}
/* Fade-in escalonado */
.content-section {
animation: fadeInUp var(--duration) var(--ease-out) backwards;
}
.content-section:nth-child(1) {
animation-delay: 0.1s;
}
.content-section:nth-child(2) {
animation-delay: 0.2s;
}
.content-section:nth-child(3) {
animation-delay: 0.3s;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
<button class="btn">Acción principal</button>
<section class="content-section">
<h2>Sección A</h2>
<p>Aparece inmediatamente.</p>
</section>
<section class="content-section">
<h2>Sección B</h2>
<p>Aparece tras 0.1s.</p>
</section>
<section class="content-section">
<h2>Sección C</h2>
<p>Aparece tras 0.2s.</p>
</section>
Nota para docentes: Demostración primero. Pregunta: ¿Qué animamos y por qué? ¿Qué pasa con movimiento reducido?
Demostración: Abrir demo interactiva
🎯 Objetivos de Aprendizaje
- Entender la animación como comunicación - Movimiento con propósito, no decoración
- Dominar técnicas de animación CSS - Transiciones, keyframes, transforms y funciones de temporización
- Aplicar mejores prácticas de rendimiento - Aceleración por hardware, will-change y movimiento reducido
- Diseñar animaciones accesibles - Respetando preferencias de usuario y carga cognitiva
- Pensar críticamente sobre el movimiento - Cuándo la animación mejora vs. distrae de la experiencia de usuario
- Construir animaciones listas para producción - Desde micro-interacciones hasta secuencias complejas
🎭 Perspectiva Crítica: La Animación como Lenguaje
“La animación no trata de hacer que las cosas se muevan. Trata de hacer que las cosas se sientan vivas.” — Diseñador UX Anónimo
Antes de codificar una sola transición, pausemos y pensemos críticamente sobre por qué animamos:
Lo Bueno: Movimiento que Sirve
- Proporciona retroalimentación - Botón presionado, formulario enviado, datos cargando
- Guía la atención - “Mira aquí después” navegación espacial
- Revela relaciones - Padre-hijo, causa-efecto, antes-después
- Deleita a usuarios - Personalidad, voz de marca, conexión emocional
- Reduce carga cognitiva - Transiciones suaves previenen desorientación
Lo Malo: Movimiento que Daña
- Distrae del contenido - Spinners gratuitos, todo rebotando
- Dispara trastornos vestibulares - Parallax, rotación sin
prefers-reduced-motion - Ralentiza interacción - Animaciones largas bloqueando progreso del usuario
- Drena batería - Animaciones ineficientes en dispositivos móviles
- Excluye usuarios - Sin alternativa para sensibilidad al movimiento
🤔 Pregunta Crítica Atelier #1
Antes de animar cualquier cosa en tu proyecto, pregunta:
- Propósito: ¿Qué necesidad del usuario sirve este movimiento?
- Alternativas: ¿Podría el diseño estático comunicar lo mismo?
- Costo: ¿Cuál es el trade-off rendimiento/accesibilidad?
- Significado: ¿Este movimiento se alinea con la voz de mi proyecto?
Documenta tus respuestas en tu diario de proyecto (mensaje de commit o DESIGN-DECISIONS.md).
📜 La Evolución de la Animación Web
1995-2005 ━━━━━━━━━━━━━━━━━ Edad Oscura
│ • Tags <blink>, <marquee> (deprecados)
│ • Animaciones GIF (limitadas, pesadas)
│ • Flash (propietario, inaccesible)
│
2006-2010 ━━━━━━━━━━━━━━━━━ Era jQuery
│ • .animate(), .slideDown()
│ • Impulsado por JavaScript (problemas rendimiento)
│ • Inconsistencia entre navegadores
│
2011-2015 ━━━━━━━━━━━━━━━━━ Revolución CSS3
│ • transition, @keyframes
│ • transform (acelerado por GPU)
│ • Propiedades animation-*
│
2016-2020 ━━━━━━━━━━━━━━━━━ Declarativo Moderno
│ • Web Animations API (WAAPI)
│ • Intersection Observer
│ • Variables CSS en animaciones
│ • Experimentos API Houdini
│
2021-HOY ━━━━━━━━━━━━━━━━━ Maduro y Accesible
• View Transitions API
• Animaciones impulsadas por scroll
• Estándar prefers-reduced-motion
• Presupuestos de rendimiento para movimiento
“Las animaciones CSS ganaron porque pusieron el control del movimiento donde pertenece: en la capa de presentación, no la capa de comportamiento.” — CSS-Tricks, adaptado
🎓 Teoría: Los 3 Pilares de la Animación CSS
Pilar 1: Transiciones (Estado → Estado)
Qué: Interpolación suave entre dos valores de propiedad.
Cuándo: Estados activados por usuario (hover, focus, active) o cambios de clase.
Sintaxis:
.element {
transition: property duration timing-function delay;
}
Ejemplo:
.button {
background: #3b82f6;
color: white;
transition: background 0.3s ease, transform 0.15s ease-out;
}
.button:hover {
background: #2563eb;
transform: translateY(-2px);
}
.button:active {
transform: translateY(0);
transition-duration: 0.1s; /* Más rápido al hacer clic */
}
🔑 Perspectiva Clave: Las transiciones son reactivas - necesitan un disparador (hover, cambio de clase, actualización de estado JS).
Pilar 2: Keyframes (Coreografía Multi-Paso)
Qué: Define pasos intermedios en una secuencia de animación.
Cuándo: Animaciones complejas, efectos en bucle, independientes de interacción de usuario.
Sintaxis:
@keyframes animation-name {
from {
/* o 0% */
}
25% {
}
50% {
}
to {
/* o 100% */
}
}
.element {
animation: animation-name duration timing-function delay iteration-count direction fill-mode;
}
Ejemplo - Fade-in al cargar página:
<section class="content-section">
<h2>Bienvenida</h2>
<p>Este bloque aparece suavemente al cargar.</p>
</section>
<section class="content-section">
<h2>Características</h2>
<p>Escalona con pequeños retrasos.</p>
</section>
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.content-section {
animation: fadeInUp 0.6s ease-out forwards;
}
/* Escalonar con delays */
.content-section:nth-child(1) {
animation-delay: 0.1s;
}
.content-section:nth-child(2) {
animation-delay: 0.2s;
}
.content-section:nth-child(3) {
animation-delay: 0.3s;
}
🔑 Perspectiva Clave: Los keyframes son proactivos - se ejecutan automáticamente al aplicarse.
Pilar 3: Transforms (Manipulación Espacial)
Qué: Mover, rotar, escalar o sesgar elementos sin activar reflow de layout.
Por qué: Acelerado por GPU = rendimiento suave a 60fps.
Funciones Principales:
transform: translateX(100px); /* Mover horizontalmente */
transform: translateY(-50px); /* Mover verticalmente */
transform: translate(50px, -20px); /* Ambos a la vez */
transform: scale(1.2); /* Crecer 20% */
transform: scaleX(0.8); /* Comprimir horizontalmente */
transform: rotate(45deg); /* Rotar en sentido horario */
transform: skewX(10deg); /* Inclinar */
/* Combinar múltiples transforms */
transform: translateX(50px) rotate(15deg) scale(1.1);
Ejemplo - Efecto de elevación de tarjeta:
.card {
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), /* Rebote */ box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-8px) scale(1.02);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
}
🔑 Perspectiva Clave: Siempre usa transform y opacity para animaciones - son solo-compositor (sin repintados).
⚡ Rendimiento: La Optimización “Will-Change”
Problema: El primer frame de animación puede tartamudear (navegador preparando capa GPU).
Solución: Decirle al navegador por adelantado qué se animará.
.button {
will-change: transform, opacity;
/* Navegador crea capa GPU inmediatamente */
}
/* ⚠️ ¡No abusar! */
.everything {
will-change: auto; /* Por defecto - solo optimizar lo que anima */
}
Mejor Práctica:
/* Añadir will-change solo cuando sea necesario */
.card:hover,
.card:focus-within {
will-change: transform;
}
Prohibido:
/* ❌ MALO - desperdicia memoria en cada elemento */
* {
will-change: transform;
}
♿ Accesibilidad: Respetando Preferencias de Movimiento del Usuario
Realidad: ~35% de usuarios experimentan mareo por animaciones (Asociación de Trastornos Vestibulares).
Solución: Media query prefers-reduced-motion.
/* Animación completa para usuarios que la toleran */
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.loader {
animation: spin 1s linear infinite;
}
/* Respetar preferencia de usuario */
@media (prefers-reduced-motion: reduce) {
.loader {
animation: none;
/* Proporcionar alternativa estática */
opacity: 0.6;
}
* {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
Enfoque de Mejora Progresiva:
/* Comenzar sin movimiento (predeterminado seguro) */
.element {
opacity: 1;
}
/* Añadir movimiento solo si usuario permite */
@media (prefers-reduced-motion: no-preference) {
.element {
animation: fadeIn 0.5s ease;
}
}
🤔 Pregunta Crítica Atelier #2
El movimiento no es inclusivo por defecto.
Prueba tus animaciones con:
- Configuración del sistema: Habilita “Reducir Movimiento” en accesibilidad del SO
- DevTools: Chrome/Firefox tienen emulación de movimiento
- Usuarios reales: Pregunta a alguien con sensibilidad vestibular
Requisito de commit: Todas las animaciones DEBEN tener alternativa prefers-reduced-motion.
🏗️ Taller Atelier: Construyendo Sistemas de Animación
Configuración: Iniciador de Utilidades de Animación
Crea src/styles/animations.css en tu proyecto:
/* ============================================
SISTEMA DE ANIMACIÓN
Tokens de Diseño + Animaciones Reutilizables
============================================ */
/* --- Tokens de Temporización --- */
:root {
--duration-instant: 100ms;
--duration-fast: 200ms;
--duration-normal: 300ms;
--duration-slow: 500ms;
--duration-slower: 800ms;
--ease-in: cubic-bezier(0.4, 0, 1, 1);
--ease-out: cubic-bezier(0, 0, 0.2, 1);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
--ease-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
--ease-elastic: cubic-bezier(0.68, -0.35, 0.265, 1.35);
}
/* --- Reset Base --- */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* --- Clases de Utilidad --- */
.fade-in {
animation: fadeIn var(--duration-normal) var(--ease-out);
}
.slide-in-up {
animation: slideInUp var(--duration-slow) var(--ease-out);
}
.scale-in {
animation: scaleIn var(--duration-fast) var(--ease-bounce);
}
/* --- Biblioteca de Keyframes --- */
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes slideInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(0.8);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@keyframes pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
Importa en tu src/main.css:
@import './styles/animations.css';
🎨 Ejercicio 1: Micro-Interacciones (Estados de Botón)
**✅ CSS Puro - No Requiere JavaScript Sin Dependencias de Frameworks**
Objetivo: Añadir retroalimentación deliciosa a botones usando solo CSS.
Dónde: styles/button.css (o cualquier página HTML + archivo CSS)
Paso 1: HTML Base
<button class="btn btn-primary">Botón Primario</button>
<button class="btn btn-secondary">Botón Secundario</button>
<button class="btn btn-primary" disabled>Botón Deshabilitado</button>
Paso 2: Estilos Base y Variantes
/* styles/button.css */
/* --- Estilos Base del Botón --- */
.btn {
position: relative;
overflow: hidden;
padding: 0.75rem 1.5rem;
border: none;
border-radius: 6px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: transform var(--duration-fast) var(--ease-out), box-shadow var(--duration-fast) var(--ease-out);
}
/* --- Variantes de Color --- */
.btn-primary {
background-color: var(--color-primary);
color: white;
}
.btn-secondary {
background-color: var(--color-secondary);
color: white;
}
/* --- Animaciones de Interacción --- */
/* Elevación al hover */
.btn:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
/* Presión activa */
.btn:active:not(:disabled) {
transform: translateY(0);
transition-duration: var(--duration-instant);
}
/* Animación de anillo de foco */
.btn:focus-visible {
outline: 2px solid currentColor;
outline-offset: 2px;
animation: pulseRing 0.6s ease-out;
}
@keyframes pulseRing {
0% {
outline-offset: 2px;
outline-color: currentColor;
}
100% {
outline-offset: 6px;
outline-color: transparent;
}
}
/* Efecto ripple (avanzado) */
.btn::after {
content: '';
position: absolute;
inset: 0;
background: rgba(255, 255, 255, 0.3);
border-radius: inherit;
transform: scale(0);
opacity: 0;
}
.btn:active:not(:disabled)::after {
animation: ripple 0.6s ease-out;
}
@keyframes ripple {
0% {
transform: scale(0);
opacity: 1;
}
100% {
transform: scale(1);
opacity: 0;
}
}
/* Estado deshabilitado */
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Accesibilidad: movimiento reducido */
@media (prefers-reduced-motion: reduce) {
.btn {
transition-duration: 0.01ms !important;
}
.btn::after,
.btn:focus-visible {
animation: none !important;
}
}
Notas para Docentes
- Demostrar hover/active/focus con teclado y ratón.
- Preguntar: ¿Qué propiedades animan? ¿Por qué
transformy notop? - Activar “Reducir movimiento” y verificar ausencia de movimiento distrayente.
- Extensión: revisar contraste de color y visibilidad de foco.
🎨 Ejercicio 2: Animaciones de Carga de Página (Fade-In Escalonado)
✅ CSS Puro - No Requiere JavaScript
Objetivo: El contenido aparece con gracia al cargar la página usando solo CSS.
Dónde: Cualquier página HTML + styles/animations.css
HTML:
<main>
<section class="content-section">
<h2>Sobre Mí</h2>
<p>Primera sección aparece inmediatamente...</p>
</section>
<section class="content-section">
<h2>Mi Trabajo</h2>
<p>Segunda sección aparece 0.1s después...</p>
</section>
<section class="content-section">
<h2>Contacto</h2>
<p>Tercera sección aparece 0.2s después...</p>
</section>
</main>
CSS:
/* Animación fade-in para todas las secciones */
.content-section {
animation: fadeInUp var(--duration-slow) var(--ease-out) backwards;
}
/* Delays escalonados */
.content-section:nth-child(1) {
animation-delay: 0.1s;
}
.content-section:nth-child(2) {
animation-delay: 0.2s;
}
.content-section:nth-child(3) {
animation-delay: 0.3s;
}
.content-section:nth-child(4) {
animation-delay: 0.4s;
}
.content-section:nth-child(5) {
animation-delay: 0.5s;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Movimiento reducido: aparición instantánea */
@media (prefers-reduced-motion: reduce) {
.content-section {
animation: none;
}
}
Notas para Docentes
- Mantener simple: solo delays; evitar coreografías exageradas.
- Explicar
backwardspara evitar parpadeos iniciales. - Comparar con/ sin
prefers-reduced-motionactivado. - Extensión: JavaScript (Intersection Observer) solo si se requiere activar por scroll.
⚠️ Mejora Opcional con JavaScript - Animaciones Activadas por Scroll (Avanzado)
Requiere: Intersection Observer API (soporte navegador: 95%+ globalmente)
Cuándo usar: Solo si necesitas animaciones activadas al hacer scroll hacia la vista (no al cargar la página)
JavaScript (mejora opcional):
// utils/scroll-animations.js
function observeAnimations() {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add('animate-in');
observer.unobserve(entry.target);
}
});
},
{ threshold: 0.1 }
);
document.querySelectorAll('[data-animate]').forEach((el) => {
observer.observe(el);
});
}
// Inicializar cuando el DOM esté listo
document.addEventListener('DOMContentLoaded', () => {
observeAnimations();
});
HTML (con atributo data-animate):
<section class="content-section" data-animate>
<h2>Sobre Mí</h2>
<p>Contenido que aparece gradualmente al hacer scroll...</p>
</section>
CSS (para animaciones activadas por JavaScript):
/* Inicialmente oculto */
[data-animate] {
opacity: 0;
transform: translateY(30px);
}
/* Animar cuando se añade la clase .animate-in por JavaScript */
[data-animate].animate-in {
animation: fadeInUp 0.6s ease-out forwards;
}
Nota: El enfoque de CSS puro (sin JavaScript) es recomendado para la mayoría de casos. Usa JavaScript solo cuando necesites específicamente animaciones activadas por scroll.
🎨 Ejercicio 3: Estados de Carga (Pantallas Skeleton y Spinners)
✅ CSS Puro - No Requiere JavaScript
Objetivo: Mostrar progreso sin frustrar usuarios.
Opción A: Pantalla Skeleton (Preferida para Contenido)
HTML:
<article class="skeleton-card">
<div class="skeleton skeleton-image"></div>
<div class="skeleton skeleton-title"></div>
<div class="skeleton skeleton-text"></div>
<div class="skeleton skeleton-text"></div>
</article>
CSS:
/* Animación shimmer base para skeleton */
.skeleton {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: shimmer 1.5s ease-in-out infinite;
border-radius: 4px;
}
@keyframes shimmer {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
/* Variantes de skeleton */
.skeleton-image {
width: 100%;
height: 200px;
}
.skeleton-title {
width: 70%;
height: 24px;
margin: 1rem 0 0.5rem;
}
.skeleton-text {
width: 100%;
height: 14px;
margin-bottom: 0.5rem;
}
/* Card skeleton completa */
.skeleton-card {
padding: 1rem;
border: 1px solid #e5e7eb;
border-radius: 8px;
}
/* Respeta preferencias de movimiento */
@media (prefers-reduced-motion: reduce) {
.skeleton {
animation: pulse 1.5s ease-in-out infinite;
}
@keyframes pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.6;
}
}
}
Opción B: Spinner (Usar con Moderación)
Usar solo para esperas cortas (<3 segundos). Prefiere pantallas skeleton para carga de contenido.
HTML:
<div class="spinner" role="status" aria-label="Cargando contenido"></div>
<p class="sr-only">Cargando contenido, por favor espera...</p>
CSS:
.spinner {
width: 40px;
height: 40px;
border: 4px solid rgba(0, 0, 0, 0.1);
border-left-color: var(--color-primary, #3b82f6);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
/* Texto solo para lectores de pantalla */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
/* Respeta preferencias de movimiento */
@media (prefers-reduced-motion: reduce) {
.spinner {
animation: pulse 1.5s ease-in-out infinite;
}
@keyframes pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
}
🤔 Pregunta Crítica Atelier #3
Las animaciones de carga son una señal de fallo.
Si los usuarios ven tu loader a menudo:
- Tu app es demasiado lenta (optimiza backend/assets)
- No estás usando mejora progresiva
- Estás priorizando “verse ocupado” sobre “ser rápido”
Mejor: UI instantánea con actualizaciones optimistas, luego sincronizar en segundo plano.
Documenta en tu proyecto: ¿Cómo minimizaste estados de carga?
🎨 Ejercicio 4: Animaciones SVG (Íconos y Logos)
✅ CSS Puro + SVG - No Requiere JavaScript
Objetivo: Animar gráficos SVG usando CSS puro para animaciones profesionales y escalables.
Efecto de Dibujo de Path SVG
HTML (SVG inline):
<svg class="logo-animated" width="200" height="200" viewBox="0 0 200 200">
<path class="logo-path" d="M 50 100 Q 100 50 150 100 T 250 100" fill="none" stroke="currentColor" stroke-width="3" />
</svg>
CSS:
.logo-path {
stroke-dasharray: 1000;
stroke-dashoffset: 1000;
animation: draw 2s ease-out forwards;
}
@keyframes draw {
to {
stroke-dashoffset: 0;
}
}
/* Respeta preferencias de movimiento */
@media (prefers-reduced-motion: reduce) {
.logo-path {
animation: none;
stroke-dashoffset: 0;
}
}
Cómo funciona:
stroke-dasharraycrea trazo discontinuostroke-dashoffsetoculta el trazo inicialmente- La animación lleva
stroke-dashoffseta 0, “dibujando” el path
Metamorfosis de Íconos SVG
HTML:
<svg class="icon-morph" width="48" height="48" viewBox="0 0 24 24">
<circle class="circle-to-square" cx="12" cy="12" r="8" fill="currentColor" />
</svg>
CSS:
.circle-to-square {
animation: morph-shape 3s ease-in-out infinite;
transform-origin: center;
}
@keyframes morph-shape {
0%,
100% {
d: path('M12,4 a8,8 0 1,0 0,16 a8,8 0 1,0 0,-16'); /* Círculo */
}
50% {
d: path('M4,4 h16 v16 h-16 z'); /* Cuadrado */
}
}
Patrón de Fondo SVG Animado
HTML:
<div class="hero-section">
<svg class="bg-pattern" width="100%" height="100%">
<defs>
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<circle cx="20" cy="20" r="2" fill="rgba(59, 130, 246, 0.1)" />
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#grid)" />
</svg>
<h1>Contenido sobre fondo animado</h1>
</div>
CSS:
.bg-pattern {
position: absolute;
inset: 0;
z-index: -1;
animation: pattern-slide 20s linear infinite;
}
@keyframes pattern-slide {
to {
transform: translate(40px, 40px);
}
}
/* Hero con posicionamiento relativo */
.hero-section {
position: relative;
min-height: 400px;
display: flex;
align-items: center;
justify-content: center;
}
🎯 Práctica Avanzada: Formas Metamorfoseantes (CSS clip-path)
✅ CSS Puro - No Requiere JavaScript
Objetivo: Animar entre diferentes formas tipo SVG.
HTML:
<button class="morph-button">Pasar el cursor para metamorfosis</button>
<div class="shape"></div>
.morph-button {
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
transition: clip-path 0.5s var(--ease-in-out);
}
.morph-button:hover {
clip-path: polygon(0 10%, 100% 0, 100% 90%, 0 100%);
}
/* Metamorfosis círculo a cuadrado */
.shape {
width: 100px;
height: 100px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
clip-path: circle(50%);
transition: clip-path 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
.shape:hover {
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}
Casos de uso:
- Transformaciones de menú de navegación
- Efectos de revelación de tarjetas
- Visualizaciones de datos interactivas
- Secciones de contenido dirigidas por arte
🎯 Práctica Avanzada: Animaciones de Texto (Typewriter, Glitch, Gradient)
Efecto Máquina de Escribir
HTML:
<h2 class="typewriter">Diseña con movimiento, no con distracción.</h2>
.typewriter {
font-family: monospace;
overflow: hidden;
border-right: 2px solid;
white-space: nowrap;
animation: typing 3.5s steps(40, end), blink-caret 0.75s step-end infinite;
}
@keyframes typing {
from {
width: 0;
}
to {
width: 100%;
}
}
@keyframes blink-caret {
from,
to {
border-color: transparent;
}
50% {
border-color: currentColor;
}
}
Efecto Glitch
HTML:
<h2 class="glitch" data-text="GLITCH">GLITCH</h2>
.glitch {
position: relative;
animation: glitch 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94) both infinite;
}
.glitch::before,
.glitch::after {
content: attr(data-text);
position: absolute;
inset: 0;
}
.glitch::before {
left: 2px;
text-shadow: -2px 0 #ff00de;
clip: rect(24px, 550px, 90px, 0);
animation: glitch-anim 2s infinite linear alternate-reverse;
}
.glitch::after {
left: -2px;
text-shadow: -2px 0 #00fff9, 2px 2px #ff00de;
animation: glitch-anim 2s infinite linear alternate-reverse;
}
@keyframes glitch-anim {
0% {
clip: rect(13px, 9999px, 94px, 0);
}
5% {
clip: rect(92px, 9999px, 61px, 0);
}
10% {
clip: rect(69px, 9999px, 40px, 0);
}
100% {
clip: rect(76px, 9999px, 19px, 0);
}
}
Texto con Gradiente Animado
HTML:
<h2 class="gradient-text">Gradiente animado</h2>
.gradient-text {
background: linear-gradient(45deg, #12c2e9, #c471ed, #f64f59, #12c2e9);
background-size: 300% 300%;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: gradientShift 3s ease infinite;
}
@keyframes gradientShift {
0%,
100% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
}
🎯 Práctica Avanzada: Animaciones Impulsadas por Scroll (CSS Moderno)
✅ CSS Puro - ¡Nuevo en 2024!
Soporte de navegador: Chrome 115+, Safari 17+ (a 2025) - verifica caniuse.com > Cuándo usar: Mejora progresiva para navegadores modernos
Objetivo: ¡Animar basado en posición de scroll SIN JavaScript!
HTML:
<div class="hero">
<h1>Haz scroll para ver efecto parallax</h1>
</div>
<div class="reading-progress"></div>
CSS:
/* Fondo parallax */
.hero {
animation: parallax linear;
animation-timeline: scroll();
}
@keyframes parallax {
to {
transform: translateY(50vh);
}
}
/* Barra de progreso de lectura */
.reading-progress {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(to right, #667eea, #764ba2);
transform-origin: left;
animation: progressBar linear;
animation-timeline: scroll();
}
@keyframes progressBar {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}
⚠️ Fallback JavaScript para Navegadores Antiguos (Opcional)
Usar solo si necesitas soportar navegadores sin soporte para
animation-timeline
// utils/scroll-fallback.js
if (!CSS.supports('animation-timeline: scroll()')) {
// Fallback JavaScript para navegadores antiguos
window.addEventListener('scroll', () => {
const scrolled = window.scrollY;
const total = document.body.scrollHeight - window.innerHeight;
const progress = scrolled / total;
document.querySelector('.reading-progress').style.transform = `scaleX(${progress})`;
});
}
🏆 Desafío Experto: View Transitions API
⚠️ JavaScript Requerido - API de Navegador de Vanguardia
Soporte de navegador: Chrome 111+, Edge 111+ (a 2025) Cuándo usar: Transiciones avanzadas de páginas SPA (no alcanzable solo con CSS)
El Futuro: ¡Transiciones suaves entre páginas en SPAs y MPAs!
// Verificar soporte del navegador
if (document.startViewTransition) {
document.startViewTransition(() => {
// Actualizar DOM (ej: navegar a nueva vista)
renderNewView();
});
} else {
// Fallback: navegación instantánea
renderNewView();
}
CSS:
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 0.5s;
}
/* Transiciones personalizadas para elementos específicos */
.hero-image {
view-transition-name: hero;
}
::view-transition-old(hero),
::view-transition-new(hero) {
animation-duration: 1s;
animation-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
📚 Lista de Verificación de Rendimiento de Animación
Antes de hacer commit de animaciones a tu proyecto:
- Solo animar
transformyopacity(acelerado por GPU) - Usar
will-changecon moderación (solo en hover/interacción) - Implementar
prefers-reduced-motion(requisito de accesibilidad) - Probar en dispositivos móviles (impacto batería/rendimiento)
- Evitar animar
width/height/top/left(dispara layout) - Usar
requestAnimationFrame()para animaciones JS - Debounce listeners de scroll (si usando JavaScript)
- Establecer presupuestos de animación (máx 3-4 animaciones simultáneas)
Pruebas de rendimiento:
# Chrome DevTools > Performance
# Grabar → Interactuar → Verificar:
# - Caída de frames (debajo de 60fps)
# - Operaciones Layout/Paint
# - Fugas de memoria (heap creciente)
🎓 Reflexión Atelier: Cuándo NO Animar
La animación es una herramienta, no un requisito. Considera diseño estático cuando:
- El contenido es prioridad - No distraigas de lectura/tareas críticas
- Dispositivos de baja gama - Respeta CPU/batería limitada
- Contextos profesionales - Sitios bancarios, salud, legales favorecen confianza sobre deleite
- Preocupaciones de accesibilidad - Sensibilidad al movimiento, carga cognitiva, lectores de pantalla
- Presupuesto de rendimiento - Has alcanzado tu tamaño de bundle / tiempo de carga
🤔 Pregunta Crítica Atelier #4
“Muévete rápido y rompe cosas” es una mentira.
En tu diario de proyecto, reflexiona:
- ¿Qué animaciones eliminaste después de probar?
- ¿Qué alternativas estáticas funcionaron mejor?
- ¿Cómo experimentaron tu movimiento usuarios con discapacidades?
Objetivo: El 80% de tu UX debería funcionar perfectamente con CSS y animaciones deshabilitadas.
🌍 Estudios de Caso del Mundo Real
Bueno: Retroalimentación Sutil de Stripe
- Hover de botón: elevación 2px + sombra (50ms)
- Validación de formulario: Checkmark verde fade-in (200ms)
- Transiciones de página: Cross-fade (300ms)
- Por qué funciona: Rápido, con propósito, mejora confianza
Malo: Sitios Portfolio Principios 2010
- Cada elemento rotando al hacer scroll
- Parallax sobre parallax
- Texto escribiéndose carácter por carácter
- Por qué falló: Lento, distractor, inductor de náusea
Excelente: Páginas de Producto Apple
- Revelaciones de producto impulsadas por scroll
- Temporización precisa (sincronizada con narrativa)
- Alto rendimiento (solo transform/opacity)
- Por qué es ejemplar: El movimiento sirve a la narrativa, no al ego
📝 Asignación: Anima Tu Portafolio
Requisitos:
-
Añade 3 tipos de animaciones a tu proyecto:
- Micro-interacción (hover botón/link)
- Animación de carga de página (fade-in escalonado)
- Estado de carga (skeleton o spinner)
-
Rendimiento:
- Solo
transformyopacity - Probar en móvil (sin jank)
- Documentar métricas de rendimiento
- Solo
-
Accesibilidad:
- Implementar
prefers-reduced-motion - Probar con movimiento deshabilitado
- Asegurar estados de foco claros
- Implementar
-
Reflexión Crítica:
- Escribir 2-3 párrafos en
DESIGN-DECISIONS.md:- ¿Por qué elegiste estas animaciones?
- ¿Qué alternativas consideraste?
- ¿Cómo mejoran la UX?
- Escribir 2-3 párrafos en
Commit:
git add src/styles/animations.css src/views/ DESIGN-DECISIONS.md
git commit -m "feat: Añadir sistema de animación a portafolio
Animaciones:
- Micro-interacciones de botón (elevación hover + ripple)
- Fade-in escalonado de carga de página (delays 0.1s)
- Carga skeleton para tarjetas de proyecto
Rendimiento:
- Todas las animaciones usan solo transform/opacity
- will-change solo en elementos interactivos
- Probado: 60fps en iPhone SE 2020
Accesibilidad:
- Fallback prefers-reduced-motion (transiciones instantáneas)
- Indicadores de foco con animación de pulso
- Navegación por teclado probada
Reflexión en DESIGN-DECISIONS.md:
- El movimiento sirve a retroalimentación, no decoración
- Eliminé parallax tras pruebas vestibulares
- Diseño estático funciona mejor para CTA crítico"
🎯 Desafíos Opcionales Avanzados
Para Estudiantes Enfocados en Diseño:
Desafío 1: Emoción Mediante Movimiento
- Crea 3 estilos de botón que transmitan emociones diferentes:
- Juguetón (rebote, wiggle)
- Serio (sutil, preciso)
- Energético (pulso, escala)
- Entregable: Codepen + justificación de diseño de 1 página
Desafío 2: Sistema de Animación de Marca
- Define tokens de diseño de movimiento para una marca (Airbnb, Nike, tu proyecto)
- Entregable: Variables CSS para duraciones, easings, patrones
Para Estudiantes Enfocados en Código:
Desafío 3: Web Animations API (WAAPI) ⚠️ JavaScript Requerido
Cuándo usar: Animaciones complejas que requieren control programático (play/pause/reverse)
- Reescribe una animación CSS usando JavaScript:
// utils/waapi-example.js
const element = document.querySelector('.animated-element');
const animation = element.animate(
[
{ transform: 'scale(1)', opacity: 1 },
{ transform: 'scale(1.2)', opacity: 0.8 },
{ transform: 'scale(1)', opacity: 1 },
],
{
duration: 600,
easing: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)',
iterations: Infinity,
}
);
// Controlar animaciones programáticamente
animation.pause();
animation.play();
animation.reverse();
- Entregable: Demo interactivo + comparación de rendimiento (CSS vs WAAPI)
- Comparar: Uso de CPU, tasa de frames, tamaño del bundle
Desafío 4: Animaciones Intersection Observer ⚠️ JavaScript Requerido
Alternativa CSS pura: Usa
animation-delayal cargar página (ver Ejercicio 2) Usa JavaScript solo si: Necesitas animaciones activadas por scroll (no animaciones de carga)
- Animar secciones solo cuando visibles en viewport
- Entregable: Función utilidad
animateOnScroll()reutilizable
Para Estudiantes Avanzados:
Desafío 5: Animaciones Basadas en Física ⚠️ JavaScript + Biblioteca Requerida
Bibliotecas: Popmotion, Framer Motion, React Spring Tamaño del bundle: ~10-30KB (considera trade-offs de rendimiento)
- Implementar física de resorte para movimiento natural
- Entregable: Interfaz drag-and-drop de sensación natural
- Reflexiona: ¿Valió la pena el tamaño del bundle? ¿CSS podría haber logrado resultados similares?
Desafío 6: Animaciones de Path SVG ✅ CSS Puro
Recomendado: ¡Esto es alcanzable con CSS puro! (ver Ejercicio 4)
- Animar
<path>de SVG usandostroke-dasharrayystroke-dashoffset - Entregable: Logo o icono animado que se dibuja al cargar la página
- Bonus: Añadir fallback para
prefers-reduced-motion
📚 Aprendizaje Adicional
Lectura Esencial
- Material Design Motion: motion.material.io
- Refactoring UI: Capítulo de animación (principios de diseño)
- Val Head: Designing Interface Animation (O’Reilly)
- Rachel Nabors: Animation at Work (A Book Apart)
Herramientas y Playgrounds
- Cubic Bezier: cubic-bezier.com - Editor de curvas de easing
- Animista: animista.net - Biblioteca de animaciones CSS
- Lottie: airbnb.design/lottie - After Effects a web
- GSAP: greensock.com/gsap - Biblioteca de animación JavaScript
Estándares y Especificaciones
- MDN: Animaciones CSS: mdn.io/css-animations
- WCAG 2.1: Animación y Movimiento (Criterio de Éxito 2.3.3)
- View Transitions API: github.com/WICG/view-transitions
🎓 Reflexión Final Atelier
“La mejor animación es invisible. Los usuarios deberían sentir el flujo, no estudiar la técnica.” — Anónimo
Al completar esta lección, has aprendido:
- ✅ Dominio técnico - Transiciones CSS, keyframes, transforms
- ✅ Conciencia de rendimiento - Aceleración GPU, will-change, movimiento reducido
- ✅ Compromiso con accesibilidad - Preferencias de movimiento, carga cognitiva
- ✅ Pensamiento crítico - Cuándo animar (y cuándo no)
- ✅ Práctica profesional - Sistemas de diseño, documentación, testing
Ahora estás equipado para hacer la web más deliciosa Y más inclusiva.
📝 Commit Tu Aprendizaje
git add .
git commit -m "feat: Completar dominio de Animaciones Web
Teoría:
- Entender transiciones, keyframes, transforms
- Dominar funciones de temporización y easing
- Aprender técnicas de optimización de rendimiento
Práctica:
- Construido sistema de animación con tokens de diseño
- Implementadas micro-interacciones (botones)
- Creados estados de carga (skeleton, spinner)
- Añadidas animaciones de carga de página (escalonadas)
Accesibilidad:
- Todas las animaciones respetan prefers-reduced-motion
- Probado con movimiento deshabilitado
- Decisiones de accesibilidad documentadas
Reflexión Crítica:
- La animación sirve a propósito, no decoración
- Presupuesto de rendimiento aplicado
- Usuarios con trastornos vestibulares considerados
Portafolio: Ahora tiene movimiento delicioso y accesible ✨"
🎨 Filosofía Atelier: “La animación no es decoración. Es comunicación. Muévete con propósito. Deleita con moderación. Siempre pregunta: ¿este movimiento sirve a mis usuarios, o solo a mi ego?”
— Prof. Rubén Vega Balbás, PhD
Próximos Pasos:
- Revisa Módulos JavaScript para organizar código de animación modularmente
- Estudia las guías de accesibilidad (WCAG 2.1) para movimiento y animación
- Explora herramientas de optimización de rendimiento (Chrome DevTools, Lighthouse)
- Practica técnicas de animación SVG para logos e íconos
- Aprende más sobre
scroll()timeline de CSS para animaciones impulsadas por scroll
¡Hagamos de la web un ecosistema virtual hermoso Y útil! 🌐✨