Las dos pelotas salen del mismo punto y llegan al mismo sitio en 45 fotogramas. Lo único distinto es cómo se reparte la velocidad por el camino.
Cada marca es la posición en un fotograma. Donde las marcas se separan, la pelota va rápido; donde se amontonan, va despacio.
◆ Dale play y comparaUna vista de la línea de tiempo que dibuja cómo cambia cada propiedad animada. En el gráfico de valores, la altura es el valor: píxeles, grados, porcentaje. En el gráfico de velocidad, cuánto cambia ese valor por segundo.
Cada fotograma clave tiene dos controles de dirección: uno para la llegada y otro para la salida. Su inclinación es la velocidad; su largo, la influencia, es decir, cuánto tiempo tira de la curva.
En el gráfico de velocidad los mismos controles se leen de otro modo: la altura es la velocidad y el largo sigue siendo la influencia.
◆ Arrastra los controles amarillosLas cinco pelotas recorren la misma distancia en el mismo tiempo. Las curvas son de valores: el tiempo corre hacia la derecha y la posición sube.
Animación › Interpolación de fotogramas clave (Ctrl+Alt+K) elige el tipo de la llegada y el de la salida por separado.
Mientras Posición va unida, X e Y comparten un solo gráfico de velocidad: aceleran y frenan a la vez, y la pelota viaja en línea recta.
Separadas, cada una tiene su curva. Con X constante e Y frenando al subir, la trayectoria se dobla en un arco.
Clic derecho sobre Posición › Separar dimensiones. Aparecen Posición X y Posición Y, cada una con sus fotogramas clave.
Un archivo de ExtendScript, el JavaScript de After Effects. Se ejecuta una sola vez y crea la composición, las capas y los fotogramas clave. Cada ejercicio trae el suyo, así que el trabajo empieza directamente en el editor.
Archivo › Secuencias de comandos › Ejecutar archivo de secuencias de comandos. Esc detiene uno en marcha.
Unas líneas pegadas a una propiedad que recalculan su valor en cada fotograma, a partir del tiempo, de otra capa o de los fotogramas clave que ya tenga. No añade ninguno.
Alt+clic en el cronómetro de la propiedad, y se pega el texto en el campo que se abre.
Cada ejercicio trae las dos: la curva se dibuja a mano, y la expresión llega al mismo movimiento por otra vía.
Nada que tenga masa arranca a toda velocidad ni se detiene en seco: gana velocidad al salir y la pierde al llegar.
// 01 · Easing
// Dos pelotas con el mismo recorrido y el mismo tiempo, las dos con
// fotogramas clave lineales. El ejercicio: suavizar la segunda.
// Archivo › Secuencias de comandos › Ejecutar archivo de secuencias de comandos
(function () {
app.beginUndoGroup("01 Easing");
var comp = app.project.items.addComp("01 Easing", 1920, 1080, 1, 3, 30);
comp.bgColor = [0.953, 0.953, 0.922];
function contenido(capa) {
return capa.property("ADBE Root Vectors Group").property(1).property("ADBE Vectors Group");
}
function pelota(nombre, color) {
var capa = comp.layers.addShape();
capa.name = nombre;
capa.property("ADBE Root Vectors Group").addProperty("ADBE Vector Group");
contenido(capa).addProperty("ADBE Vector Shape - Ellipse");
contenido(capa).property(1).property("ADBE Vector Ellipse Size").setValue([110, 110]);
contenido(capa).addProperty("ADBE Vector Graphic - Fill");
contenido(capa).property(2).property("ADBE Vector Fill Color").setValue(color);
return capa;
}
function recorrido(capa, y) {
var tr = capa.property("ADBE Transform Group");
tr.property("ADBE Position").dimensionsSeparated = true;
tr.property("ADBE Position_1").setValue(y);
var x = tr.property("ADBE Position_0");
x.setValueAtTime(0, 300);
x.setValueAtTime(1.5, 1620);
for (var i = 1; i <= x.numKeys; i++) {
x.setInterpolationTypeAtKey(i, KeyframeInterpolationType.LINEAR);
}
}
recorrido(pelota("Lineal", [0.62, 0.6, 0.72, 1]), 380);
recorrido(pelota("Suavizado", [0.337, 0.196, 0.855, 1]), 700);
comp.openInViewer();
app.endUndoGroup();
})();
// En Posición X de «Suavizado»: ease() suaviza entre dos valores ease(time, 0, 1.5, 300, 1620)
ease() suaviza siempre con la misma curva, parecida a la de
F9, y no trae nada para graduarla: el 80 % de influencia solo se consigue en
el editor.
Los fotogramas entre dos poses deciden el peso. Cada bote sube menos que el anterior, así que también dura menos.
En Posición Y el suelo queda arriba del gráfico: en After Effects la Y crece hacia abajo.
◆ Arrastra los fotogramas clave// 02 · Sincronización (Timing)
// La pelota y el suelo. Posición va con las dimensiones separadas: X avanza
// en línea recta e Y tiene ocho fotogramas clave, uno cada 10 fotogramas.
// El ejercicio: llevar cada uno a su fotograma y suavizar las cimas.
// Archivo › Secuencias de comandos › Ejecutar archivo de secuencias de comandos
(function () {
app.beginUndoGroup("02 Sincronización");
var comp = app.project.items.addComp("02 Sincronización", 1920, 1080, 1, 3, 30);
comp.bgColor = [0.953, 0.953, 0.922];
var f = comp.frameDuration;
function contenido(capa) {
return capa.property("ADBE Root Vectors Group").property(1).property("ADBE Vectors Group");
}
function forma(nombre, elipse, tam, color) {
var capa = comp.layers.addShape();
capa.name = nombre;
capa.property("ADBE Root Vectors Group").addProperty("ADBE Vector Group");
if (elipse) {
contenido(capa).addProperty("ADBE Vector Shape - Ellipse");
contenido(capa).property(1).property("ADBE Vector Ellipse Size").setValue(tam);
} else {
contenido(capa).addProperty("ADBE Vector Shape - Rect");
contenido(capa).property(1).property("ADBE Vector Rect Size").setValue(tam);
}
contenido(capa).addProperty("ADBE Vector Graphic - Fill");
contenido(capa).property(2).property("ADBE Vector Fill Color").setValue(color);
return capa;
}
// El suelo, justo debajo de la pelota cuando su centro está en 880
var suelo = forma("Suelo", false, [1920, 8], [0.09, 0.08, 0.2, 1]);
suelo.property("ADBE Transform Group").property("ADBE Position").setValue([960, 948]);
var pelota = forma("Pelota", true, [128, 128], [0.337, 0.196, 0.855, 1]);
var tr = pelota.property("ADBE Transform Group");
tr.property("ADBE Position").dimensionsSeparated = true;
var x = tr.property("ADBE Position_0");
var y = tr.property("ADBE Position_1");
x.setValueAtTime(0, 200);
x.setValueAtTime(70 * f, 1720);
// Cimas y suelos alternados. La Y crece hacia abajo: el suelo es el valor alto.
var valores = [250, 880, 520, 880, 680, 880, 790, 880];
for (var i = 0; i < valores.length; i++) {
y.setValueAtTime(i * 10 * f, valores[i]);
}
for (var k = 1; k <= x.numKeys; k++) x.setInterpolationTypeAtKey(k, KeyframeInterpolationType.LINEAR);
for (k = 1; k <= y.numKeys; k++) y.setInterpolationTypeAtKey(k, KeyframeInterpolationType.LINEAR);
comp.openInViewer();
app.endUndoGroup();
})();
// En Posición Y: el rebote calculado con gravedad
// y con la energía que se pierde en cada bote
var suelo = 880, altura = 630, e = .75, g = 5040;
var v = Math.sqrt(2 * g * altura); // velocidad al tocar el suelo
var t = time - v / g; // tiempo desde el primer impacto
var y = suelo;
if (t < 0) {
y = suelo - altura + g * time * time / 2;
} else {
v *= e;
while (v > 30 && t > 2 * v / g) { t -= 2 * v / g; v *= e; }
if (v > 30) y = suelo - (v * t - g * t * t / 2);
}
y
Calcula tiempos y alturas con la gravedad: cambiando e se
rehace el rebote entero. A cambio, ya no hay fotogramas clave que retocar uno por uno.
Antes de salir, el objeto retrocede un poco, y al llegar se pasa y regresa. El retroceso avisa al ojo de lo que viene. El sobrepaso da peso a la frenada.
// 03 · Anticipación (Anticipation)
// Un cuadrado con Posición separada y dos fotogramas clave en Posición X,
// a 0.3 y 1.1 segundos. El ejercicio: que retroceda antes de salir y se
// pase al llegar, solo con los controles del editor.
// Archivo › Secuencias de comandos › Ejecutar archivo de secuencias de comandos
(function () {
app.beginUndoGroup("03 Anticipación");
var comp = app.project.items.addComp("03 Anticipación", 1920, 1080, 1, 2, 30);
comp.bgColor = [0.953, 0.953, 0.922];
var capa = comp.layers.addShape();
capa.name = "Cuadrado";
capa.property("ADBE Root Vectors Group").addProperty("ADBE Vector Group");
function contenido() {
return capa.property("ADBE Root Vectors Group").property(1).property("ADBE Vectors Group");
}
contenido().addProperty("ADBE Vector Shape - Rect");
contenido().property(1).property("ADBE Vector Rect Size").setValue([150, 150]);
contenido().property(1).property("ADBE Vector Rect Roundness").setValue(26);
contenido().addProperty("ADBE Vector Graphic - Fill");
contenido().property(2).property("ADBE Vector Fill Color").setValue([0.655, 0.169, 0.788, 1]);
var tr = capa.property("ADBE Transform Group");
tr.property("ADBE Position").dimensionsSeparated = true;
tr.property("ADBE Position_1").setValue(540);
var x = tr.property("ADBE Position_0");
x.setValueAtTime(0.3, 500);
x.setValueAtTime(1.1, 1400);
for (var i = 1; i <= x.numKeys; i++) {
x.setInterpolationTypeAtKey(i, KeyframeInterpolationType.LINEAR);
}
comp.openInViewer();
app.endUndoGroup();
})();
// En Posición X: al llegar al último fotograma clave, sigue
// el impulso que traía y se amortigua
var amp = .06, frec = 2.2, amort = 6;
var n = numKeys, x = value;
if (n > 0 && time > key(n).time) {
var t = time - key(n).time;
var v = velocityAtTime(key(n).time - thisComp.frameDuration / 2);
x = value + v * amp * Math.sin(frec * t * 2 * Math.PI) / Math.exp(amort * t);
}
x
Resuelve solo la llegada, y solo si el cuadrado llega con velocidad: se prueba con los fotogramas clave en lineal. Con F9 llega a velocidad 0 y no queda impulso que continuar.
La pelota se alarga mientras cae, en la dirección del movimiento, y se aplasta al chocar. Lo que gana de alto lo pierde de ancho, así que el volumen se mantiene.
| Fotograma | 10 | 14 | 15 | 18 | 22 |
|---|---|---|---|---|---|
| X | 100 | 85 | 139 | 94 | 100 |
| Y | 100 | 118 | 72 | 106 | 100 |
// 04 · Estirar y encoger (Squash and stretch)
// La pelota ya rebota: cimas suavizadas e impactos lineales. Su punto de
// anclaje está en la base, para que se aplaste contra el suelo y no contra
// el aire. Lleva cinco fotogramas clave de Escala al 100 % alrededor del
// primer impacto. El ejercicio: darles la deformación.
// Archivo › Secuencias de comandos › Ejecutar archivo de secuencias de comandos
(function () {
app.beginUndoGroup("04 Estirar y encoger");
var comp = app.project.items.addComp("04 Estirar y encoger", 1920, 1080, 1, 3, 30);
comp.bgColor = [0.953, 0.953, 0.922];
var f = comp.frameDuration;
function contenido(capa) {
return capa.property("ADBE Root Vectors Group").property(1).property("ADBE Vectors Group");
}
function forma(nombre, elipse, tam, color) {
var capa = comp.layers.addShape();
capa.name = nombre;
capa.property("ADBE Root Vectors Group").addProperty("ADBE Vector Group");
if (elipse) {
contenido(capa).addProperty("ADBE Vector Shape - Ellipse");
contenido(capa).property(1).property("ADBE Vector Ellipse Size").setValue(tam);
} else {
contenido(capa).addProperty("ADBE Vector Shape - Rect");
contenido(capa).property(1).property("ADBE Vector Rect Size").setValue(tam);
}
contenido(capa).addProperty("ADBE Vector Graphic - Fill");
contenido(capa).property(2).property("ADBE Vector Fill Color").setValue(color);
return capa;
}
var suelo = forma("Suelo", false, [1920, 8], [0.09, 0.08, 0.2, 1]);
suelo.property("ADBE Transform Group").property("ADBE Position").setValue([960, 948]);
var pelota = forma("Pelota", true, [128, 128], [0.337, 0.196, 0.855, 1]);
var tr = pelota.property("ADBE Transform Group");
tr.property("ADBE Anchor Point").setValue([0, 64]); // la base de la pelota
tr.property("ADBE Position").dimensionsSeparated = true;
var x = tr.property("ADBE Position_0");
var y = tr.property("ADBE Position_1");
x.setValueAtTime(0, 200);
x.setValueAtTime(66 * f, 1720);
x.setInterpolationTypeAtKey(1, KeyframeInterpolationType.LINEAR);
x.setInterpolationTypeAtKey(2, KeyframeInterpolationType.LINEAR);
// El rebote del ejercicio 2, medido en la base: suelo en 944
var fotos = [0, 15, 26, 38, 46, 55, 60, 66];
var valores = [314, 944, 584, 944, 744, 944, 854, 944];
var i;
for (i = 0; i < fotos.length; i++) y.setValueAtTime(fotos[i] * f, valores[i]);
for (i = 1; i <= y.numKeys; i++) {
if (i % 2 == 1) { // cimas: desaceleración y aceleración suaves
y.setInterpolationTypeAtKey(i, KeyframeInterpolationType.BEZIER);
y.setTemporalEaseAtKey(i, [new KeyframeEase(0, 33.33)], [new KeyframeEase(0, 33.33)]);
} else { // impactos: lineales
y.setInterpolationTypeAtKey(i, KeyframeInterpolationType.LINEAR);
}
}
// Escala plana alrededor del primer impacto (fotograma 15)
var escala = tr.property("ADBE Scale");
var marcas = [10, 14, 15, 18, 22];
for (i = 0; i < marcas.length; i++) escala.setValueAtTime(marcas[i] * f, [100, 100]);
for (i = 1; i <= escala.numKeys; i++) {
escala.setInterpolationTypeAtKey(i, KeyframeInterpolationType.LINEAR);
}
comp.openInViewer();
app.endUndoGroup();
})();
// En Escala: se estira según la velocidad vertical, // y lo que gana de alto lo pierde de ancho var vy = transform.yPosition.velocity; var k = Math.min(Math.abs(vy) / 5000, .3); [100 / (1 + k), 100 * (1 + k)]
Estira según la velocidad. El aplastamiento ocurre en el único fotograma en que la velocidad cambia de signo, y ahí la expresión no tiene qué medir: sigue siendo trabajo de fotogramas clave.
Los elementos de un grupo entran uno detrás de otro, separados por unos pocos fotogramas. Ese retraso dice que forman una lista y en qué orden se leen.
// 05 · Offset & Delay
// Cinco barras que entran desde la derecha, las cinco a la vez y con
// fotogramas clave lineales. El ejercicio: suavizar la llegada y separarlas
// unos fotogramas entre sí.
// Archivo › Secuencias de comandos › Ejecutar archivo de secuencias de comandos
(function () {
app.beginUndoGroup("05 Offset & Delay");
var comp = app.project.items.addComp("05 Offset & Delay", 1920, 1080, 1, 3, 30);
comp.bgColor = [0.953, 0.953, 0.922];
var f = comp.frameDuration;
function contenido(capa) {
return capa.property("ADBE Root Vectors Group").property(1).property("ADBE Vectors Group");
}
// De la 5 a la 1: cada capa nueva queda arriba, así «Barra 1» termina con
// el índice 1 y la expresión puede contar la distancia por índice.
for (var n = 5; n >= 1; n--) {
var capa = comp.layers.addShape();
capa.name = "Barra " + n;
capa.property("ADBE Root Vectors Group").addProperty("ADBE Vector Group");
contenido(capa).addProperty("ADBE Vector Shape - Rect");
contenido(capa).property(1).property("ADBE Vector Rect Size").setValue([900, 72]);
contenido(capa).property(1).property("ADBE Vector Rect Roundness").setValue(36);
contenido(capa).addProperty("ADBE Vector Graphic - Fill");
var t = (n - 1) / 4; // del violeta al magenta
contenido(capa).property(2).property("ADBE Vector Fill Color")
.setValue([0.337 + 0.318 * t, 0.196 - 0.027 * t, 0.855 - 0.067 * t, 1]);
var pos = capa.property("ADBE Transform Group").property("ADBE Position");
pos.setValueAtTime(0, [2400, 180 + n * 120]);
pos.setValueAtTime(24 * f, [960, 180 + n * 120]);
pos.setInterpolationTypeAtKey(1, KeyframeInterpolationType.LINEAR);
pos.setInterpolationTypeAtKey(2, KeyframeInterpolationType.LINEAR);
}
comp.openInViewer();
app.endUndoGroup();
})();
// En Posición de Barra 2 a Barra 5: repite a Barra 1
// con 3 fotogramas de retraso por cada capa de distancia
var guia = thisComp.layer("Barra 1");
var retraso = (index - guia.index) * 3 * thisComp.frameDuration;
var x = guia.transform.position.valueAtTime(time - retraso)[0];
[x, value[1]]
Las barras copian la curva de Barra 1. Basta retocar esa curva, o cambiar el 3, para cambiar el ritmo de las cinco.
La mano hereda todo lo que le pasa al brazo, que es su capa principal, sin un fotograma clave propio. Si además llega un poco tarde, el gesto parece hecho de piezas unidas por articulaciones.
// 06 · Parenting
// «Brazo», con el punto de anclaje en el hombro y un giro de 0° a −70°,
// y «Mano», suelta en la punta. El ejercicio: emparentar la mano, darle al
// brazo un sobrepaso y que la mano llegue unos fotogramas tarde.
// Archivo › Secuencias de comandos › Ejecutar archivo de secuencias de comandos
(function () {
app.beginUndoGroup("06 Parenting");
var comp = app.project.items.addComp("06 Parenting", 1920, 1080, 1, 2, 30);
comp.bgColor = [0.953, 0.953, 0.922];
var f = comp.frameDuration;
function contenido(capa) {
return capa.property("ADBE Root Vectors Group").property(1).property("ADBE Vectors Group");
}
function barra(nombre, tam, redondez, color) {
var capa = comp.layers.addShape();
capa.name = nombre;
capa.property("ADBE Root Vectors Group").addProperty("ADBE Vector Group");
contenido(capa).addProperty("ADBE Vector Shape - Rect");
contenido(capa).property(1).property("ADBE Vector Rect Size").setValue(tam);
contenido(capa).property(1).property("ADBE Vector Rect Roundness").setValue(redondez);
contenido(capa).addProperty("ADBE Vector Graphic - Fill");
contenido(capa).property(2).property("ADBE Vector Fill Color").setValue(color);
return capa;
}
// El brazo mide 440: con el ancla a 200 del centro, gira desde el hombro
var brazo = barra("Brazo", [440, 72], 36, [0.235, 0.294, 0.686, 1]);
var tb = brazo.property("ADBE Transform Group");
tb.property("ADBE Anchor Point").setValue([-200, 0]);
tb.property("ADBE Position").setValue([640, 620]);
var giro = tb.property("ADBE Rotate Z");
giro.setValueAtTime(0, 0);
giro.setValueAtTime(24 * f, -70);
giro.setInterpolationTypeAtKey(1, KeyframeInterpolationType.LINEAR);
giro.setInterpolationTypeAtKey(2, KeyframeInterpolationType.LINEAR);
// La mano, con el ancla en la muñeca y colocada en la punta del brazo
var mano = barra("Mano", [130, 96], 30, [0.655, 0.169, 0.788, 1]);
var tm = mano.property("ADBE Transform Group");
tm.property("ADBE Anchor Point").setValue([-65, 0]);
tm.property("ADBE Position").setValue([1040, 620]);
comp.openInViewer();
app.endUndoGroup();
})();
// En Rotación de «Mano», ya emparentada: repite el giro
// del brazo con 4 fotogramas de retraso
var brazo = thisComp.layer("Brazo").transform.rotation;
var d = 4 * thisComp.frameDuration;
value + brazo.valueAtTime(time - d) - brazo.value
Trabaja sobre el emparentado: sin capa principal, la mano giraría sola en su sitio, lejos del brazo.
Todo con una pelota y el editor abierto de principio a fin.
0 de 7
Cada respuesta explica por qué, acierte o no.
| En el editor | |
|---|---|
| Abrir o cerrar el editor de gráficos | Mayús+F3 |
| Desacel./Acel. suave | F9 |
| Desaceleración suave, solo la llegada | Mayús+F9 |
| Aceleración suave, solo la salida | Ctrl+Mayús+F9 |
| Velocidad de fotogramas clave | Ctrl+Mayús+K |
| Interpolación de fotogramas clave | Ctrl+Alt+K |
| Dividir los controles de un fotograma clave | Alt+arrastrar |
| En la línea de tiempo | |
|---|---|
| Mostrar solo las propiedades animadas | U |
| Fotograma clave anterior y siguiente | J K |
| Alternar entre lineal y curva automática | Ctrl+clic |
| Conmutar mantener fotograma clave | Ctrl+Alt+H |
| Mover la capa un fotograma | Alt+Av Pág Alt+Re Pág |
En macOS, Cmd en lugar de Ctrl y Opción en lugar de Alt. Una expresión se añade con Alt+clic en el cronómetro de la propiedad.
Los doce principios de la animación. De ahí salen Sincronización, Anticipación, Estirar y encoger, y Acelerar y desacelerar.
Los doce principios del motion graphics. Easing, Offset & Delay y Parenting, con sus laboratorios.
Introducción a After Effects: la pelota, parte 1 y parte 2. El rebote completo, con el programa delante.
El principio de arcos, animado. La trayectoria curva que aparece al separar dimensiones.
Contadores dinámicos en After Effects. Más expresiones, esta vez aplicadas a cifras.
Adobe. (2024a, 3 de octubre). Secuencias de comandos. Guía del usuario de After Effects. https://helpx.adobe.com/es/after-effects/desktop/automate-in-after-effects/automate-animation/scripts.html
Adobe. (2024b, 4 de noviembre). Velocidad. Guía del usuario de After Effects. https://helpx.adobe.com/es/after-effects/desktop/animate-in-after-effects/speed-between-keyframes/speed.html
Adobe. (2026, 5 de mayo). Interpolación de fotogramas clave. Guía del usuario de After Effects. https://helpx.adobe.com/es/after-effects/desktop/animate-in-after-effects/animation-keyframes/keyframe-interpolation.html
Thomas, F. y Johnston, O. (1981). The illusion of life: Disney animation. Abbeville Press.
Willenskomer, I. (2017, 31 de marzo). Creating usability with motion: The UX in motion manifesto. UX in Motion. https://medium.com/ux-in-motion/creating-usability-with-motion-the-ux-in-motion-manifesto-a87a4584ddc
Williams, R. (2001). The animator's survival kit. Faber and Faber.
Esta presentación está pensada para pantalla horizontal.