Skip to main content

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ígida
decay = 6; // amortecimento — mais alto = para mais rápido
n = 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

MaterialfreqdecaySensação
Gelatina22muito mole, longa oscilação
Borracha45bounce firme mas natural
Metal83vibração rápida, decaimento lento
Elástico tenso68bounce rápido que para logo
Água1.51.5ondulação suave e longa

Overshoot proporcional à velocidade

// mais dinâmico — bounce maior quando o objeto chega mais rápido
damping = 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 Y
gravity = 2000; // pixels/s²
bounciness = 0.6; // energia mantida por quique (0-1)
groundY = 800; // coordenada Y do chão
initialVelY = 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 vertical
var 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 arrastar
smoothing = 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, Bounciness
ctrl = thisComp.layer("[PHYSICS CONTROLS]");
freq = ctrl.effect("Spring Freq")("Slider");
decay = ctrl.effect("Spring Decay")("Slider");
// ... usa essas variáveis nas expressões

Referência por situação

SituaçãoAbordagem
UI/UX animationspring leve (freq 3-5, decay 8-12)
motion brandingovershoot proporcional
personagem 2Dsquash & stretch exagerado, bounce longo
data vizinércia suave
impacto/explosãoshake decrescente + spring rígido