📝 La firma de interpolate()
O interpolate() recibe cuatro argumentos: o valor de entrada (casi siempre frame), o inputRange, o outputRange y opciones opcionales. A cambio, devuelve un número calculado mediante interpolación lineal entre los puntos.
import { interpolate, useCurrentFrame } from "remotion"; const frame = useCurrentFrame(); // interpolate(valor, inputRange, outputRange, opcoes?) const opacity = interpolate( frame, // entrada: número atual [0, 30], // inputRange: do frame 0 ao 30 [0, 1], // outputRange: de 0 a 1 (opacidade) { extrapolateLeft: "clamp", extrapolateRight: "clamp" } );
🎯 Qué hace cada argumento
- •frame — el número que cambia en cada cuadro; puede ser cualquier variable numérica.
- •inputRange — la ventana de fotogramas en la que ocurre la animación (al menos 2 puntos).
- •outputRange — los valores de salida correspondientes; pueden ser píxeles, grados, decimales.
- •opciones — controla lo que ocurre fuera del inputRange (clamp, extend, wrap).
🗺️ inputRange y outputRange — dominio y codominio
Piensa en inputRange como el dominio (qué frames forman parte de la animación) y outputRange como el contradominio (qué valores se obtienen). El interpolate() encuentra el punto proporcionalmente equivalente dentro del outputRange.
📊 Ejemplos de mapeo
[0, 30] → [0, 1]— fade-in: la opacidad crece de 0 a 1 en 1 segundo (30fps).[0, 30] → [0, 300]— deslizar: translateX va de 0 a 300px en 1 segundo.[0, 30] → [0.5, 1]— escala: el objeto comienza a la mitad de su tamaño y crece hasta el 100%.[0, 30] → [360, 0]— rotación inversa: comienza girado 360° y llega a 0.
const opacity = interpolate(frame, [0, 20], [0, 1], { extrapolateRight: "clamp" }); const translateY = interpolate(frame, [0, 20], [40, 0], { extrapolateRight: "clamp" }); return ( <div style={{ opacity, transform: `translateY(${translateY}px)` }}> Conteúdo que entra de baixo </div> );
💡 Consejo: los rangos no necesitan empezar en 0
Puedes usar [45, 60] como inputRange para que la animación comience 1,5s después del inicio del video. Lo que queda fuera del rango se controla con la opción extrapolate.
🔒 extrapolate: clamp vs extend
Qué sucede cuando frame sale de inputRange? Por defecto, el interpolate() extiende la línea más allá de los puntos definidos — lo que puede hacer que la opacidad supere 1 o baje de 0. El clamp evita esto.
✓ Qué HACER
- ✓Usar
extrapolateRight: "clamp"en opacidad para que nunca supere 1. - ✓Usar
extrapolateLeft: "clamp"para mantener el valor antes de que comience la animación. - ✓Dejar
extend(predeterminado) solo cuando la extrapolación tenga sentido (p. ej., una posición que continúa).
✗ Qué NO hacer
- ✗Dejar la opacidad sin
clamp— en el frame 60,[0,30]→[0,1]devuelve 2 (invisible en CSS). - ✗Olvidar el
extrapolateLeftcuando la animación empieza tarde (frame 45+). - ✗Usar opciones diferentes de
clamp/extend/wrap/identity— no existen.
// SEM clamp: frame=60 → opacity=2 (bug visual) const opacityBug = interpolate(frame, [0, 30], [0, 1]); // COM clamp: frame=60 → opacity=1 (correto) const opacity = interpolate(frame, [0, 30], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp", });
🔑 Múltiples keyframes — fade-in / hold / fade-out
O inputRange e o outputRange aceptan más de dos puntos. Esto crea keyframes: la animación pasa por cada par [frame → valor] en secuencia. Es como el timeline de un editor de video, pero en código.
frame 0 → opacity 0
Elemento invisible al inicio.
frame 15 → opacity 1
Fade-in completo en 0,5s.
frame 75 → opacity 1
Permanece totalmente visible hasta los 2,5s: el "hold".
frame 90 → opacity 0
Fade-out de 0,5s antes del corte.
const opacity = interpolate( frame, [0, 15, 75, 90], // 4 keyframes [0, 1, 1, 0], // entra, segura, sai { extrapolateLeft: "clamp", extrapolateRight: "clamp" } );
🎨 Easing — movimiento natural
Por defecto, el interpolate() hace una interpolación linear — velocidad constante de principio a fin. Para un movimiento natural, usa la opción easing con funciones del módulo Easing de Remotion (compatible con React Native).
🎯 Easings más usados
- •
Easing.out(Easing.ease)— empieza rápido y se desacelera al final. Clásica para entradas. - •
Easing.in(Easing.ease)— empieza despacio y acelera. Buena para salidas. - •
Easing.inOut(Easing.ease)— suave en ambos extremos. Sólido para transiciones. - •
Easing.bezier(0.25, 0.1, 0.25, 1)— control total con curva cúbica. - •
Easing.cubic— alternativa rápida a bezier con curva cúbica estándar.
import { interpolate, Easing, useCurrentFrame } from "remotion"; const frame = useCurrentFrame(); const translateY = interpolate(frame, [0, 25], [60, 0], { extrapolateRight: "clamp", easing: Easing.out(Easing.cubic), });
💡 Lineal vs. easing en la práctica
Una animación lineal parece «mecánica» — el objeto se mueve a velocidad robótica. Con Easing.out, el elemento entra con energía y desacelera naturalmente, como si obedeciera a la física. Esa diferencia es lo que separa los prototipos de los videos profesionales.
🧩 Patrones: fade, slide y scale
Con interpolate() a mano, tres patrones cubren el 90% de las animaciones de UI de video: fade (opacidad), deslizar (translateX/Y) y escala. Al combinar los tres, puedes construir cualquier entrada o salida de un elemento.
✓ Los tres patrones esenciales
- ✓Fade:
opacity = interpolate(frame, [0,20], [0,1], {clamp}) - ✓Deslizamiento X:
transform: translateX(${x}px)con x de −200 a 0. - ✓Scale:
transform: scale(${s})con s de 0.8 a 1.
✗ Errores comunes
- ✗Usar CSS transitions o keyframes: entra en conflicto con el modelo de render de Remotion.
- ✗Combinar
scaleetranslateXsintransformen una sola string (se borran). - ✗Usar
width/heightanimados en lugar detransform: scale(provoca reflow).
const opts = { extrapolateRight: "clamp", easing: Easing.out(Easing.cubic) } as const; const opacity = interpolate(frame, [0, 20], [0, 1], opts); const x = interpolate(frame, [0, 20], [-60, 0], opts); const scale = interpolate(frame, [0, 20], [0.9, 1], opts); return ( <div style={{ opacity, transform: `translateX(${x}px) scale(${scale})`, }}> Conteúdo com entrada suave </div> );
💡 Combina siempre en el mismo transform
Cuando usas transform dos veces en el mismo objeto de estilo, el segundo sobrescribe el primero. Siempre concatena todo en una sola cadena: `translateX(${x}px) scale(${s})`.
📌 Resumen del módulo
Easing.out(Easing.cubic) hace que el movimiento sea natural; combínalo con los patrones fade/slide/scale.Próximo módulo:
1.3 — spring() & composición: física de resorte, overshoot natural y cómo apilar escenas con Sequence y AbsoluteFill.