wandres.dev
PRINCIPIOS DEL MOVIMIENTO · De la animación clásica a la UI

El peso y la masa aparente

Cómo la curva, la duración y la deformación construyen la sensación de masa, y por qué un objeto pesado que se mueve rápido rompe la ilusión.

⏱ 17 min

Un panel modal de novecientos píxeles de ancho y un icono de veinticuatro no pueden moverse igual, aunque nada en la especificación de CSS lo prohíba. El sistema visual atribuye masa a los objetos que ve moverse, y la atribuye a partir de tres señales medibles: cuánto tarda en arrancar, cuánto tarda en parar y si se deforma al hacerlo. Cuando esas señales contradicen el tamaño aparente, el resultado no se percibe como “una animación con otra curva”: se percibe como algo que va mal y que casi nadie sabe nombrar.

🎯 Al terminar esta lección sabrás
  • Enunciar las tres señales que el sistema visual usa para atribuir masa.
  • Calcular una duración a partir de la distancia recorrida en lugar de copiarla.
  • Elegir la curva correcta según qué extremo del movimiento importa.
  • Aplicar deformación por aceleración en la magnitud en que funciona.

La masa se lee en el arranque, no en el recorrido

Cuando un objeto empieza a moverse, la información sobre su masa está casi toda en los primeros cien milisegundos. Un objeto ligero alcanza su velocidad de crucero casi de inmediato; uno pesado tarda, porque hace falta más impulso para acelerar más masa. Esa asimetría es lo que codifica la primera mitad de una función de suavizado.

En términos de Bézier cúbica, el primer punto de control gobierna el arranque. Un primer punto con el componente horizontal alto —cubic-bezier(0.6, 0, ...)— produce un arranque perezoso: el progreso apenas avanza mientras el tiempo corre. Eso es masa. Un primer punto con el componente horizontal bajo —cubic-bezier(0.05, 0.7, ...)— produce un arranque explosivo, que es lo contrario: un objeto ligero o algo que responde a un impulso directo.

La segunda mitad codifica otra cosa distinta: cómo se detiene. Y aquí hay una asimetría importante que casi nunca se explica. Frenar es una acción, arrancar es una reacción. Un objeto que llega a su destino y se para en seco se lee como que ha chocado. Uno que desacelera largamente se lee como que ha llegado por su propio impulso. Por eso las curvas con salida larga —ease-out, cubic-bezier(0.2, 0, 0, 1)— dominan en interfaces: casi todo lo que se mueve en una interfaz llega a un sitio y se queda, y llegar suavemente es lo que corresponde.

La combinación de las dos mitades produce una tabla de intenciones que conviene tener presente:

Perfil Curva típica Se lee como
Entrada, sale de fuera y se queda cubic-bezier(0.2, 0, 0, 1) Llega por su propio impulso
Salida, se va y desaparece cubic-bezier(0.4, 0, 1, 1) Es empujado fuera
Movimiento entre dos puntos visibles cubic-bezier(0.4, 0, 0.2, 1) Se traslada con masa
Respuesta a una pulsación cubic-bezier(0.05, 0.7, 0.1, 1) Reacciona sin inercia

Las curvas de salida son deliberadamente más bruscas al final. Un elemento que se va no necesita aterrizar con delicadeza porque no aterriza: desaparece. Alargar su final es tiempo que el usuario espera mirando algo que ya no le interesa.

La duración depende de la distancia, no del componente

Copiar 300ms de un componente a otro es el error de temporización más extendido, y su causa es que se piensa en la duración como una propiedad del componente cuando es una propiedad del recorrido.

El sistema visual lee velocidad. Si un elemento recorre 40 píxeles en 300 milisegundos va a 133 píxeles por segundo; si otro recorre 800 en el mismo tiempo va a 2666. Esa diferencia de veinte veces se percibe, y el segundo se lee como algo lanzado, no como algo que se desplaza.

La relación correcta no es lineal. Duplicar la distancia no debe duplicar la duración, porque los objetos reales tienen una velocidad de crucero: aceleran, viajan y frenan, y la fase de viaje escala con la distancia mientras las otras dos son casi constantes. La aproximación que se usa en la práctica es una raíz, y es fácil de expresar en CSS moderno:

:root {
  /* Duracion base para un recorrido de referencia de 100px. */
  --dur-base: 200ms;
  --dist-ref: 100;
}

.mover {
  /* dur = base * sqrt(dist / ref), aproximada con pow. */
  --dist: 400;
  transition-duration: calc(
    var(--dur-base) * pow(calc(var(--dist) / var(--dist-ref)), 0.5)
  );
}

Con esos números, 100 píxeles dan 200 milisegundos y 400 píxeles dan 400, no 800. La progresión se comprime, que es exactamente lo que hace un objeto con velocidad terminal.

Cuando la distancia solo se conoce en ejecución, el mismo cálculo desde JavaScript, y aquí se puede afinar más porque tienes el número exacto:

// Duracion en funcion de la distancia, con suelo y techo.
function duracion(distanciaPx, base = 200, ref = 100) {
  const d = base * Math.sqrt(Math.max(distanciaPx, 1) / ref);
  return Math.min(Math.max(d, 120), 520);
}

const dy = destino.top - origen.top;
tarjeta.animate(
  [{ translate: `0 ${-dy}px` }, { translate: '0 0' }],
  { duration: duracion(Math.abs(dy)), easing: 'cubic-bezier(0.2, 0, 0, 1)' }
);

El suelo y el techo no son adornos. Por debajo de unos 120 milisegundos el movimiento deja de leerse como movimiento y se lee como un parpadeo, así que no aporta información y sí cuesta. Por encima de unos 500 el usuario ya está esperando, sea cual sea la distancia.

La masa aparente depende del área en pantalla, no del tamaño en píxeles CSS

Aquí hay una trampa que rompe sistemas de diseño enteros y que solo aparece cuando pruebas en un dispositivo real. La masa que el sistema visual atribuye a un objeto no depende de sus dimensiones en píxeles CSS, depende de cuánto ángulo visual ocupa, que es una función del tamaño físico y de la distancia a la que está el ojo. Un panel de 400 píxeles CSS en un móvil sostenido a treinta centímetros ocupa muchísimo más campo visual que los mismos 400 píxeles en un monitor a setenta centímetros. Por eso la misma animación, con la misma curva y la misma duración, se siente ágil en el escritorio y pesada y aparatosa en el móvil, y por eso la solución que todo el mundo intenta —“pongo la duración en una variable y ya”— no funciona: el problema no es que la duración deba cambiar, es que la fracción de pantalla recorrida ha cambiado sin que tú tocaras nada. Un desplazamiento de 400 píxeles es un tercio de la pantalla en escritorio y la pantalla entera en un móvil. La consecuencia operativa es que las distancias de animación no deberían expresarse en píxeles absolutos cuando el recorrido es grande: exprésalas en unidades relativas al viewport o al propio elemento, para que la fracción de campo visual recorrida se mantenga estable. Y cuando el recorrido sea grande de verdad —un panel a pantalla completa—, baja la duración en móvil en lugar de subirla, que es lo contrario de lo que dicta la intuición: recorre más campo visual, así que a igual duración va mucho más rápido en términos de ángulo por segundo, y lo que hay que compensar no es la lentitud sino el exceso de velocidad angular.

La deformación, en la magnitud en que funciona

Un objeto real se deforma cuando acelera, y esa deformación es una señal de masa muy fuerte. El problema en interfaces es la magnitud: en dibujos animados el squash and stretch llega al treinta o cuarenta por ciento, y en una interfaz un elemento deformado un cinco por ciento ya se lee como un fallo de renderizado.

El rango que funciona está entre el uno y el tres por ciento, aplicado en el eje del movimiento y solo mientras hay aceleración. Es una deformación que nadie percibe conscientemente y que sí percibe el sistema visual, que es exactamente el objetivo.

@keyframes caer {
  0%   { translate: 0 -60px; scale: 1 1; }
  60%  { translate: 0 0;     scale: 1 1.03; }  /* estirado al acelerar */
  80%  { translate: 0 0;     scale: 1.02 0.98; } /* aplastado al parar */
  100% { translate: 0 0;     scale: 1 1; }
}

.ficha-cae {
  animation: caer 340ms cubic-bezier(0.2, 0, 0, 1) both;
  transform-origin: center bottom;
}

Dos detalles no evidentes. El primero es que transform-origin importa tanto como la escala: un objeto que cae y se aplasta lo hace contra el suelo, así que el origen tiene que estar en el borde inferior; si lo dejas en el centro, el objeto se hunde en sí mismo y se lee como un efecto, no como un impacto. El segundo es que la deformación de estirado y la de aplastado deben conservar el volumen aparente: si estiras un tres por ciento en vertical, comprime aproximadamente lo mismo en horizontal. Un objeto que crece en un eje sin encoger en el otro no se lee como deformación elástica, se lee como cambio de tamaño.

La deformación es también el sitio donde más fácil es pasarse, y hay una prueba honesta para saber si te has pasado: enséñaselo a alguien y pregúntale qué ha visto. Si menciona la deformación, es demasiado grande. Debe notarse como peso, no como efecto.

⚔️ Calibra la masa de tres elementos
  1. Toma tres elementos de tamaños muy distintos —un icono, una tarjeta y un panel a pantalla completa— y anima los tres el mismo recorrido con la misma duración. Anota cuál se siente mal y por qué.
  2. Sustituye la duración fija por la fórmula de raíz sobre la distancia y repite. Ajusta el suelo y el techo hasta que los tres se sientan del mismo mundo.
  3. Añade deformación al que más se mueve, empezando por un uno por ciento y subiendo hasta que alguien la mencione. Baja un escalón desde ahí.