wandres.dev
ANIMATIONS · El panel de animaciones

La línea de tiempo del grupo: leer la coreografía

Cómo se representan retrasos, duraciones e iteraciones, qué revela la posición relativa de las barras, y los cuatro defectos de coordinación que se ven de un vistazo.

⏱ 16 min

Una transición de interfaz que se percibe como torpe casi nunca lo es por la curva de una animación individual: lo es porque cinco animaciones que deberían contarse una historia empiezan y acaban en momentos que no se corresponden con nada. La línea de tiempo del panel dibuja esas cinco barras una debajo de otra, en la misma escala, y hace visible la coreografía completa. Es la vista que convierte una impresión vaga en un defecto concreto que se puede corregir con un número.

🎯 Al terminar esta lección sabrás
  • Leer la representación de retraso, duración e iteraciones de cada animación.
  • Desplazarse por la línea de tiempo y examinar cualquier instante intermedio.
  • Reconocer los cuatro defectos de coordinación más frecuentes.
  • Saltar de una animación del panel al elemento y a la regla que la define.

Qué dibuja cada fila

Cada animación del grupo es una fila con una barra sobre la escala de tiempo compartida.

El tramo previo a la barra es el retraso. El tiempo que la animación espera antes de empezar. Se ve como espacio vacío a la izquierda.

La barra es la duración de una iteración. Su longitud sobre la escala común es lo que permite comparar dos animaciones de un vistazo.

Las repeticiones se dibujan como barras consecutivas. Una animación con tres iteraciones ocupa tres veces el ancho de una.

Los puntos sobre la barra son los fotogramas clave, cuando la animación los tiene. Su posición dice dónde ocurre cada cambio dentro del recorrido, que no siempre es donde uno cree al leer los porcentajes.

Y hay un elemento que atraviesa todas las filas: el cursor de tiempo, que se puede arrastrar. Al moverlo, la página se actualiza mostrando el estado exacto de todas las animaciones en ese instante.

💡
Tip

Arrastrar el cursor es, con diferencia, la función más útil del panel. Permite inspeccionar el estado intermedio de una transición: pararla al treinta por ciento, abrir el inspector de elementos y mirar los valores computados en ese punto. Ninguna otra herramienta permite examinar el estado de una interfaz en mitad de un movimiento.

Los cuatro defectos de coordinación

Todo a la vez. Todas las barras empiezan en la misma vertical y tienen la misma longitud. Es el defecto por defecto, literalmente: es lo que sale cuando se aplica la misma transición a varios elementos sin pensar. Produce un movimiento que se percibe como mecánico, porque en el mundo físico las cosas no se mueven todas a la vez y con la misma velocidad.

El arreglo es un escalonamiento: pequeños retrasos crecientes entre elementos relacionados. La cantidad correcta suele ser mucho menor de lo que la gente pone; unas decenas de milisegundos por elemento bastan para que la secuencia se lea como tal.

El hueco muerto. Una barra acaba y la siguiente empieza bastante después, dejando un tramo en el que no ocurre nada. Se ve inmediatamente en la línea de tiempo como un espacio vacío entre bloques. La percepción es de tartamudeo. El arreglo es solapar: que la siguiente empiece antes de que la anterior termine del todo.

El desbordamiento. Una animación mucho más larga que las demás alarga el grupo entero, y el usuario percibe la transición como terminada mucho antes de que lo esté. Es especialmente dañino cuando esa animación larga bloquea algo: si la interfaz no acepta interacción hasta que la transición acabe, el usuario espera mirando algo que ya parece quieto.

El orden invertido. Los elementos entran en un orden que no corresponde a su jerarquía visual: primero el detalle y después el contenedor, o de abajo arriba cuando el ojo lee de arriba abajo. En la línea de tiempo se ve comparando el orden vertical de las barras con el orden espacial de los elementos, y al pasar el ratón por cada fila se resalta su elemento en la página, que es la forma de comprobarlo.

Del panel al código

Cada fila del panel enlaza con su origen. Al pasar el ratón se resalta el elemento en la página, y la información de la animación indica el nombre de la regla de fotogramas clave o la propiedad de la transición.

Con eso, encontrar la declaración responsable en el panel de estilos es directo. Y en el caso de las animaciones creadas por código, el objeto de animación es accesible desde la consola con la consulta del documento, lo que permite inspeccionar sus tiempos exactos:

// Del panel al codigo: inspeccionar los tiempos exactos de una animacion
(() => {
  window.detallarAnimacion = (indice = 0) => {
    const a = document.getAnimations()[indice];
    if (!a) return console.warn('No hay animacion en el indice', indice);

    const t = a.effect.getTiming();
    const c = a.effect.getComputedTiming();
    console.log('Objetivo:', a.effect.target);
    console.table([{
      retrasoMs: t.delay,
      duracionMs: t.duration,
      retrasoFinalMs: t.endDelay,
      iteraciones: t.iterations,
      inicioIteracion: t.iterationStart,
      direccion: t.direction,
      relleno: t.fill,
      curva: t.easing
    }]);
    console.table([{
      duracionTotalMs: Math.round(c.endTime),
      progresoActual: c.progress?.toFixed(3) ?? '(fuera del tramo activo)',
      iteracionActual: c.currentIteration ?? '-',
      estado: a.playState,
      velocidad: a.playbackRate
    }]);

    // Los fotogramas clave, si el efecto es de keyframes
    if (a.effect.getKeyframes) console.table(a.effect.getKeyframes());
    return a;
  };

  window.irA = (indice, fraccion) => {
    const a = document.getAnimations()[indice];
    if (!a) return;
    const total = a.effect.getComputedTiming().endTime;
    a.pause();
    a.currentTime = total * fraccion;
    console.log('Colocada al', Math.round(fraccion * 100) + '% ->', Math.round(a.currentTime), 'ms');
    return a;
  };

  console.log('Usa detallarAnimacion(0) y irA(0, 0.35)');
})();

La distinción entre los tiempos declarados y los computados es informativa: los primeros son lo que escribiste, los segundos incluyen la resolución de valores automáticos y el tiempo total con todas las iteraciones. Cuando una animación dura más de lo que esperabas, la respuesta suele estar en esa segunda tabla.

La función que coloca una animación en una fracción concreta hace desde código lo mismo que arrastrar el cursor, y es útil cuando hace falta reproducir un estado exacto varias veces.

Un patrón de coreografía

El escalonamiento es la técnica que corrige el primero de los cuatro defectos y merece un ejemplo completo, porque hacerlo bien tiene un detalle que casi siempre se pasa por alto.

// Entrada escalonada con salida en orden inverso
function entradaEscalonada(elementos, { paso = 40, duracion = 320 } = {}) {
  return elementos.map((el, i) =>
    el.animate(
      [
        { opacity: 0, transform: 'translateY(12px)' },
        { opacity: 1, transform: 'none' }
      ],
      { duration: duracion, delay: i * paso, easing: 'cubic-bezier(.2,.8,.2,1)', fill: 'both' }
    )
  );
}

function salidaEscalonada(elementos, { paso = 25, duracion = 200 } = {}) {
  const n = elementos.length;
  return elementos.map((el, i) =>
    el.animate(
      [{ opacity: 1, transform: 'none' }, { opacity: 0, transform: 'translateY(8px)' }],
      { duration: duracion, delay: (n - 1 - i) * paso, easing: 'cubic-bezier(.4,0,1,1)', fill: 'both' }
    )
  );
}

// Ejemplo ejecutable
const contenedor = document.createElement('ul');
contenedor.style.cssText = 'list-style:none;padding:0;font:15px system-ui';
document.body.append(contenedor);
const items = ['Primero', 'Segundo', 'Tercero', 'Cuarto', 'Quinto'].map(txt => {
  const li = document.createElement('li');
  li.textContent = txt;
  li.style.cssText = 'padding:8px 12px;margin:4px 0;background:#89b4fa;color:#11111b;border-radius:8px';
  contenedor.append(li);
  return li;
});

const entrada = entradaEscalonada(items);
Promise.all(entrada.map(a => a.finished))
  .then(() => new Promise(r => setTimeout(r, 800)))
  .then(() => salidaEscalonada(items));

El detalle que casi nadie aplica es que la salida va en orden inverso y más rápida que la entrada. En orden inverso porque lo último que entró es lo que está más cerca de la atención del usuario; y más rápida porque una salida lenta se percibe como una espera, mientras que una entrada lenta se percibe como algo que se presenta. Esa asimetría entre entrada y salida es una de las diferencias más visibles entre una interfaz que se siente cuidada y otra que no, y en la línea de tiempo del panel se ve como dos bloques de formas distintas.

El defecto más común no está en ninguna animación individual sino en el espacio entre ellas

Después de examinar unas cuantas transiciones reales en este panel aparece un patrón claro: las animaciones individuales casi siempre están bien y la coreografía casi siempre está mal. Tiene sentido, porque una animación individual se prueba mirándola, se ajusta hasta que se ve bien y ahí acaba; la coordinación entre varias, en cambio, nadie la ve como un objeto: cada una se escribió en un sitio distinto del código, muchas veces por personas distintas, y nadie ha mirado nunca las cinco juntas sobre la misma escala de tiempo. El panel es el único sitio donde esa coreografía existe como una cosa observable, y por eso su valor real no es ajustar una curva sino descubrir que hay un problema de conjunto. Hay un principio de diseño de movimiento que conviene tener presente y que este panel hace verificable: el movimiento en una interfaz sirve para explicar una relación causal —esto salió de aquí, esto sustituye a aquello, esto pertenece a este grupo— y si la coreografía no cuenta esa relación, el movimiento no está informando, solo está retrasando. Con esa vara de medir, las preguntas ante una línea de tiempo se vuelven concretas. ¿El orden en que aparecen las cosas corresponde a la relación entre ellas, o es el orden en que estaban en el código? ¿La cosa que desaparece y la que la sustituye se solapan lo suficiente como para leerse como una sustitución, o hay un hueco que las convierte en dos sucesos sin relación? ¿La duración total corresponde a la importancia del cambio, o una transición trivial está ocupando medio segundo de la vida del usuario? Y una que decide más que las otras tres juntas: ¿cuánto de este movimiento está en la ruta crítica de que el usuario pueda hacer lo siguiente? Una animación de entrada de trescientos milisegundos que bloquea la interacción cuesta trescientos milisegundos a cada usuario, cada vez, para siempre; la misma animación que no bloquea nada es gratis. Esa distinción no se ve en el diseño, no se ve en el código, y se ve inmediatamente en esta línea de tiempo comparando dónde acaba la última barra con el momento en que la interfaz vuelve a aceptar entrada.