Skip to main content

Kinetic typography com expressões

Kinetic type de qualidade não é texto com keyframes. É texto que tem uma lógica: responde a um número, uma duração, um ritmo, um estado. Quando você constrói isso com expressões, o texto se torna um sistema, não uma animação fixa.

Texto que muda de escala baseado em valor

Útil para placar, contador de progresso, slide de dados: o número maior deveria visualmente tomar mais espaço.

// em Scale do text layer
ctrl = thisComp.layer("[CONTROLS]");
val = ctrl.effect("Valor")("Slider");
minScale = 60;
maxScale = 180;
minVal = 0;
maxVal = 100;
normalized = (val - minVal) / (maxVal - minVal);
normalized = Math.max(0, Math.min(1, normalized)); // clamp
scaleFactor = minScale + (maxScale - minScale) * normalized;
[scaleFactor, scaleFactor]

Texto que muda de cor baseado em valor

Três layers sobrepostos (verde, amarelo, vermelho) com opacity controlada por expressão:

// em layer VERDE:
ctrl = thisComp.layer("[CONTROLS]");
val = ctrl.effect("Valor")("Slider");
(val >= 70) ? 100 : (val >= 40) ? (val - 40) / 30 * 100 : 0
// em layer AMARELO:
(val < 40 || val > 70) ? 0 :
(val < 55) ? (val - 40) / 15 * 100 :
(70 - val) / 15 * 100
// em layer VERMELHO:
(val <= 40) ? 100 : (val <= 70) ? (70 - val) / 30 * 100 : 0

Source Text com expressão numérica

// número inteiro
ctrl = thisComp.layer("[CONTROLS]");
val = ctrl.effect("Contador")("Slider");
Math.round(val).toString()
// com padding de zeros (001, 045, 100)
n = Math.round(val);
padded = (n < 10) ? "00" + n : (n < 100) ? "0" + n : n.toString();
padded
// porcentagem com decimal
val.toFixed(1) + "%"

Timer formatado em Source Text

ctrl = thisComp.layer("[CONTROLS]");
totalSec = Math.round(ctrl.effect("Segundos")("Slider"));
mins = Math.floor(totalSec / 60);
secs = totalSec % 60;
minStr = (mins < 10) ? "0" + mins : mins.toString();
secStr = (secs < 10) ? "0" + secs : secs.toString();
minStr + ":" + secStr

Animação sincronizada com áudio

  1. Importe o arquivo de áudio
  2. Animation > Keyframe Assistant > Convert Audio to Keyframes
  3. Um nulo “Audio Amplitude” é criado com Left Channel, Right Channel, Both Channels
  4. Use esses valores como controladores
// escala reagindo ao áudio
audioLayer = thisComp.layer("Audio Amplitude");
amplitude = audioLayer.effect("Both Channels")("Slider");
normalized = Math.min(amplitude / 80, 1); // amplitude típica: 0 a 80
scale = 90 + 70 * normalized;
[scale, scale]
// com suavização temporal (evita jitter)
audioLayer = thisComp.layer("Audio Amplitude");
lookback = 0.05;
samples = 5;
smoothed = 0;
for (i = 0; i < samples; i++) {
t = time - (lookback * i / samples);
smoothed += audioLayer.effect("Both Channels")("Slider").valueAtTime(t);
}
smoothed /= samples;
scale = 90 + 70 * Math.min(smoothed / 80, 1);
[scale, scale]

Loop de texto com array de strings

// alterna entre diferentes textos em loop
textos = ["Branding", "Motion", "Identity", "Design", "Story"];
duracao = 2.5; // segundos por texto
idx = Math.floor(time / duracao) % textos.length;
textos[idx]

Fonte variável com expressão (AE 2023+)

// weight que oscila suavemente
baseWeight = 400;
variation = 300;
oscillation = Math.sin(time * 2 * Math.PI * 0.5); // 0.5 Hz
baseWeight + variation * (oscillation * 0.5 + 0.5) // de 400 a 700
// weight proporcional a slider
ctrl = thisComp.layer("[CONTROLS]");
val = ctrl.effect("Intensidade")("Slider"); // 0 a 100
minWeight = 300;
maxWeight = 900;
minWeight + (maxWeight - minWeight) * (val / 100)

Contador com easing manual

// easing cúbico out no próprio Source Text
startVal = 0;
endVal = 1250;
startTime = 1.0;
endTime = 4.0;
t = (time - startTime) / (endTime - startTime);
t = Math.max(0, Math.min(1, t));
eased = 1 - Math.pow(1 - t, 3);
currentVal = startVal + (endVal - startVal) * eased;
Math.round(currentVal).toString()

Texto que reage à posição de um nulo

// layer "[CURSOR]" como controlador de posição
cursorLayer = thisComp.layer("[CURSOR]");
cursorPos = cursorLayer.transform.position;
compCenter = [thisComp.width / 2, thisComp.height / 2];
dx = cursorPos[0] - compCenter[0];
dy = cursorPos[1] - compCenter[1];
dist = Math.sqrt(dx * dx + dy * dy);
maxDist = 400;
proximity = 1 - Math.min(dist / maxDist, 1);
scale = 80 + 40 * proximity;
[scale, scale]

Boas práticas

Sempre use um layer de controle centralizado. Sliders em um único nulo permitem ajustar toda a peça sem tocar em expressões individuais.

Separe lógica de apresentação. A expressão calcula o valor, o layer exibe. Não misture formatação de texto com cálculos complexos no Source Text.

Teste com valores extremos. Se o slider pode ir de 0 a 100, teste com -10 e 150 também. Sua expressão precisa de Math.max/Math.min para não quebrar.