Física reativa sem plugins
Animação que parece pesada não é questão de plugin. É questão de lógica. Bounce, spring, inércia — tudo isso cabe em 10 a 30 linhas de expressão.
Spring clássico
A expressão de spring que circula na comunidade há anos. Cada parte explicada:
// coloca em position (ou qualquer propriedade numérica)freq = 3; // frequência de oscilação (Hz) — mais alto = mola mais rígidadecay = 6; // amortecimento — mais alto = para mais rápidon = 0;
if (numKeys > 0) { n = nearestKey(time).index; if (key(n).time > time) n--;}
if (n == 0) { t = 0;} else { t = time - key(n).time;}
if (n > 0 && t < 1) { v = velocityAtTime(key(n).time - thisComp.frameDuration / 10); amp = v / (freq * 2 * Math.PI); value + amp * Math.exp(-decay * t) * Math.sin(freq * 2 * Math.PI * t);} else { value;}Não precisa modificar a expressão pra cada situação. Só ajusta freq e decay.
Referência por material
| Material | freq | decay | Sensação |
|---|---|---|---|
| Gelatina | 2 | 2 | muito mole, longa oscilação |
| Borracha | 4 | 5 | bounce firme mas natural |
| Metal | 8 | 3 | vibração rápida, decaimento lento |
| Elástico tenso | 6 | 8 | bounce rápido que para logo |
| Água | 1.5 | 1.5 | ondulação suave e longa |
Overshoot proporcional à velocidade
// mais dinâmico — bounce maior quando o objeto chega mais rápidodamping = 8;elasticity = 0.6;
n = 0;if (numKeys > 0) { n = nearestKey(time).index; if (key(n).time > time) n--;}
if (n > 0) { t = time - key(n).time; vel = velocityAtTime(key(n).time - thisComp.frameDuration / 10); amplitude = vel * elasticity / damping; omega = damping * 0.8; value + amplitude * Math.exp(-damping * t) * Math.sin(omega * 2 * Math.PI * t);} else { value;}Gravidade e bounce no chão
// coloca na posição Ygravity = 2000; // pixels/s²bounciness = 0.6; // energia mantida por quique (0-1)groundY = 800; // coordenada Y do chãoinitialVelY = 0;
t = time - inPoint;
vel = initialVelY;bounceTime = 0;currentT = t;
for (i = 0; i < 10; i++) { bounceDuration = 2 * vel / gravity;
if (currentT < bounceDuration || i == 9) { y = groundY - (vel * currentT - 0.5 * gravity * currentT * currentT); break; }
currentT -= bounceDuration; vel *= bounciness;
if (vel < 1) { y = groundY; break; }}
Math.min(y, groundY)Squash & stretch automático
// coloca em Scale — calcula a partir da velocidade verticalvar velY = Math.abs(velocityAtTime(time)[1]);var maxVel = 800;var factor = Math.min(velY / maxVel, 1);
var stretchAmount = 20;var squashAmount = 25;var velRaw = velocityAtTime(time)[1];
if (velRaw > 0) { // descendo — vai comprimir scaleX = 100 + squashAmount * factor; scaleY = 100 - squashAmount * factor * 0.6;} else { // subindo — alongado scaleX = 100 - stretchAmount * factor * 0.5; scaleY = 100 + stretchAmount * factor;}
[scaleX, scaleY]Inércia / follow-through
// objeto que continua se movendo depois que você para de arrastarsmoothing = 6;
if (numKeys < 2) { value;} else { n = nearestKey(time).index; if (key(n).time > time) n--;
if (n == 0) { value; } else { t = time - key(n).time; target = value; vel = velocityAtTime(key(n).time); amp = vel / (smoothing * 2 * Math.PI); inertiaOffset = amp * Math.exp(-smoothing * t) * Math.sin(smoothing * 2 * Math.PI * t); target + inertiaOffset; }}Nulo de física centralizado
// nulo [PHYSICS CONTROLS] com sliders: Spring Freq, Spring Decay, Gravity, Bouncinessctrl = thisComp.layer("[PHYSICS CONTROLS]");freq = ctrl.effect("Spring Freq")("Slider");decay = ctrl.effect("Spring Decay")("Slider");// ... usa essas variáveis nas expressõesReferência por situação
| Situação | Abordagem |
|---|---|
| UI/UX animation | spring leve (freq 3-5, decay 8-12) |
| motion branding | overshoot proporcional |
| personagem 2D | squash & stretch exagerado, bounce longo |
| data viz | inércia suave |
| impacto/explosão | shake decrescente + spring rígido |