wandres.dev
LOTTIE Y AFTER EFFECTS · La animación del diseñador

Qué hay dentro de un fichero Lottie

La anatomía del JSON: capas, transformaciones, propiedades animadas y modificadores de forma. Qué representa realmente el formato y qué es imposible meter dentro.

⏱ 19 min

Un Lottie no es un vídeo ni una imagen: es la descripción declarativa de una composición de After Effects, serializada a JSON. Dentro hay un grafo de capas con propiedades animadas por keyframes, y fuera hay un intérprete que reconstruye ese grafo fotograma a fotograma. Entender que lo que envías no es un resultado sino un programa explica de golpe casi todo lo demás: por qué pesa lo que pesa, por qué a veces va mal, y qué cosas no se pueden pedir.

🎯 Al terminar esta lección sabrás
  • Leer la estructura de un fichero .json de Lottie e identificar sus partes.
  • Distinguir una propiedad estática de una animada y leer sus keyframes con sus tangentes.
  • Reconocer qué elementos de forma son baratos y cuáles multiplican el trabajo del intérprete.
  • Enumerar lo que el formato no puede representar y por qué.

Un grafo de escena, no un render

Abre cualquier .json de Lottie con el formateador de tu editor y lo primero que ves es una cabecera corta con la metainformación de la composición.

{
  "v": "5.12.2",
  "fr": 60,
  "ip": 0,
  "op": 180,
  "w": 512,
  "h": 512,
  "nm": "Carga",
  "ddd": 0,
  "assets": [],
  "layers": [],
  "markers": []
}

fr son los fotogramas por segundo de la composición original; ip y op el primer y el último fotograma; w y h el tamaño de la composición; ddd indica si hay capas tridimensionales. Todos los tiempos del fichero están expresados en fotogramas de esta composición, no en milisegundos. Un reproductor que decida ir al segundo 1,5 tiene que multiplicar por fr para saber a qué fotograma corresponde, y ese es el motivo por el que cambiar la velocidad de reproducción de un Lottie es trivial mientras que cambiar su duración en tiempo real no lo es.

assets guarda imágenes incrustadas o referenciadas y precomposiciones, que son composiciones enteras usadas como una capa dentro de otra. Una precomposición es una llamada a función: el mismo grafo evaluado con otro contexto de tiempo. Esto importa porque una precomposición repetida no duplica bytes pero sí duplica trabajo en cada fotograma.

layers es un array ordenado de atrás hacia delante en el fichero final, aunque en After Effects se vea al revés. Cada capa tiene un tipo:

ty Tipo Coste típico
0 Precomposición El de todo lo que contiene
1 Sólido Muy bajo
2 Imagen Bajo, pero suma bytes
3 Nulo Cero, solo transforma a sus hijos
4 Forma El grueso del trabajo
5 Texto Alto si hay glifos vectorizados

Transformaciones y propiedades animadas

Toda capa lleva un objeto ks con su transformación. Los nombres son crípticos pero se aprenden en un minuto: a es el punto de anclaje, p la posición, s la escala, r la rotación y o la opacidad.

Cada una de esas propiedades tiene la misma forma, y es la pieza central del formato:

"o": { "a": 0, "k": 100 }

a: 0 significa no animada: el valor está directo en k. Y la versión animada:

"p": {
  "a": 1,
  "k": [
    { "t": 0,  "s": [256, 400], "i": { "x": [0.2], "y": [1] }, "o": { "x": [0.4], "y": [0] } },
    { "t": 45, "s": [256, 180] }
  ]
}

a: 1 significa animada, y k pasa a ser un array de keyframes. t es el fotograma, s el valor en ese fotograma, e i y o son los manejadores de entrada y salida de la curva de Bézier temporal que lleva de este keyframe al siguiente. El último keyframe no necesita tangentes porque no lleva a ninguna parte.

Tres consecuencias prácticas de este diseño.

Las curvas de easing son por keyframe y por componente. Fíjate en que i.x e i.y son arrays. En una propiedad de dos dimensiones puede haber una curva distinta para x y para y. Eso da una expresividad enorme y es exactamente lo que hace imposible reducir un Lottie a un @keyframes de CSS sin perder información.

El emparejamiento entre parent e ind construye la jerarquía. Una capa con "parent": 3 hereda la transformación de la capa cuyo ind es 3. Es un grafo de escena de manual, y el intérprete tiene que resolver el orden topológico en cada fotograma.

No hay valores computados. El fichero no dice dónde está el elemento en el segundo 1,3; dice cómo calcularlo. El intérprete evalúa todas las propiedades animadas de todas las capas en cada fotograma. Ese es, literalmente, todo el coste de runtime del formato.

Formas y modificadores

Una capa de forma tiene un array shapes con elementos identificados por ty. Los que vas a ver casi siempre:

ty Elemento Qué hace
gr Grupo Contiene otros elementos y su propia transformación
sh Trazado Un path arbitrario con vértices y tangentes
rc, el, sr Rectángulo, elipse, estrella Primitivas parametrizadas
fl, st Relleno, trazo Color, opacidad, grosor, uniones
gf, gs Relleno y trazo de gradiente Paradas de color animables
tr Transformación La del grupo, misma forma que ks
tm Trim path Recorta el trazado entre dos porcentajes
rp Repetidor Duplica el grupo N veces con un delta
mm Merge paths Operaciones booleanas entre trazados

Un trazado animado guarda la geometría completa en cada keyframe:

"ks": {
  "a": 1,
  "k": [
    { "t": 0, "s": [{
        "i": [[0, -8], [8, 0], [0, 8], [-8, 0]],
        "o": [[0, 8], [-8, 0], [0, -8], [8, 0]],
        "v": [[-14, 0], [0, -14], [14, 0], [0, 14]],
        "c": true
      }]
    }
  ]
}

v son los vértices, i y o las tangentes de cada vértice relativas al vértice, y c indica si el trazado está cerrado. Aquí es donde el peso del fichero se dispara: cada keyframe de un trazado de cien puntos son seiscientos números. Un logo complejo animado punto a punto puede ocupar megabytes de coordenadas.

Los dos modificadores que más cuestan merecen atención especial:

tm, el trim path, es el equivalente a stroke-dasharray y el truco detrás de casi todas las animaciones de “línea que se dibuja”. Es barato en bytes y en cómputo, y es la razón por la que una animación de carga con trazos suele pesar unos pocos kilobytes.

rp, el repetidor, es lo contrario. Doce copias de un grupo escritas en tres líneas de JSON, y doce grupos que el intérprete tiene que evaluar, transformar y dibujar en cada fotograma. Un fichero de veinte kilobytes con repetidores anidados puede generar más trabajo por fotograma que uno de doscientos sin ellos. El tamaño del fichero no predice el coste de runtime, y los repetidores son la razón principal.

ℹ️
Cómo contar el trabajo real de un fichero

Cuenta capas de forma, no kilobytes. Un script de diez líneas que recorra layers, sume los grupos, multiplique por el c de cada repetidor y cuente los vértices de cada trazado te dice más sobre el coste por fotograma que cualquier medida de tamaño. Un fichero con más de un centenar de trazados dibujados simultáneamente va a costar en cualquier reproductor.

Lo que no cabe dentro

Esta es la lista que hay que enseñarle al diseñador antes de que empiece, no después.

Los efectos de After Effects, casi todos. El formato admite un puñado de efectos —relleno, corrección de color simple, algún desenfoque, algún trazo— y ninguno más. Glow, partículas, desenfoques direccionales, deformaciones, todo lo que venga de un plugin de terceros: no existe. Lo que ocurre al exportar es peor que un error: el exportador ignora el efecto en silencio y el resultado se ve distinto.

Las expresiones. El exportador puede evaluarlas y hornear el resultado en keyframes, y entonces el fichero se llena de un keyframe por fotograma —recuerda: hasta seiscientos números por keyframe en un trazado— o puede intentar convertirlas, y solo un subconjunto se convierte. Una expresión con un wiggle de dos segundos a sesenta fotogramas por segundo horneada son ciento veinte keyframes de una propiedad que antes ocupaba una línea.

Los rasters de verdad. Puedes incrustar imágenes, pero se transportan en base64 dentro del JSON, lo que las infla en torno a un tercio, y no participan de la escalabilidad vectorial que era el argumento del formato.

El desenfoque de movimiento y la profundidad de campo. Son propiedades de un renderizador de vídeo. No hay nada en el formato ni en el intérprete que las reproduzca.

Las capas 3D reales. El campo ddd existe y algunas transformaciones tridimensionales se transportan, pero no hay cámara ni luces ni intersección de planos. Lo que llega es un aplanamiento, y el aplanamiento a veces no se parece a lo que se vio en After Effects.

Lottie transporta un programa, y por eso su tamaño no dice nada de su coste

La intuición que traemos de las imágenes y los vídeos —cuanto más grande el fichero, más caro— es exactamente falsa aquí, y no en los márgenes: es falsa en el orden de magnitud y en ambos sentidos. Un Lottie de ochocientos kilobytes puede ser un único trazado con miles de keyframes horneados de una expresión, que el intérprete resuelve con una interpolación por fotograma y cuesta prácticamente nada dibujar. Un Lottie de doce kilobytes puede ser un grupo con un repetidor de veinticuatro copias dentro de otro repetidor de ocho, con un trim path animado en cada copia, y eso son ciento noventa y dos trazados que hay que recalcular, transformar y rasterizar sesenta veces por segundo. La causa es que el formato no almacena fotogramas, almacena la receta para generarlos, y las recetas tienen bucles. Es el mismo fenómeno por el que un fractal de cuatro líneas de código tarda más en dibujarse que un mapa de bits de un megapíxel. La consecuencia operativa es que toda medida de Lottie basada en el tamaño del fichero es ruido, incluidos los presupuestos de rendimiento que la mayoría de los equipos escriben en su documentación de diseño. Lo único que predice el coste es el número de trazados dibujados simultáneamente en el fotograma más cargado, multiplicado por el área que cubren, y eso no se ve mirando el JSON de arriba: hay que contarlo o medirlo. Quien internaliza esto deja de pedirle al diseñador ficheros más pequeños y empieza a pedirle menos formas visibles a la vez, que es la petición que de verdad cambia el resultado, y que además el diseñador puede cumplir sin que la animación pierda nada.