Процедурные анимации в контексте Lottie Web строятся на сочетании заранее подготовленных векторных сцен и динамического управления их параметрами во время выполнения. В отличие от полностью статических JSON-анимаций, процедурный подход предполагает генерацию вариативности, поведения и состояния без пересборки исходного файла в After Effects, за счёт использования JavaScript API и структуры данных Lottie.
Lottie Web работает с JSON-описанием анимации, экспортированной из After Effects через Bodymovin. Внутри файла содержатся:
Процедурность возникает не на этапе рендеринга, а на этапе управления этим JSON-деревом после загрузки в runtime.
Основной объект управления:
const animation = lottie.loadAnimation({
container: document.getElementById('anim'),
renderer: 'svg',
loop: true,
autoplay: true,
path: 'animation.json'
});
После инициализации доступ к внутренним структурам позволяет изменять поведение анимации без изменения исходного файла.
Ключевой механизм процедурных эффектов — контроль времени воспроизведения.
animation.goToAndStop(120, true);
animation.playSegments([30, 90], true);
animation.setSpeed(1.5);
Сегментирование позволяет использовать один Lottie-файл как набор состояний. При процедурном подходе сегменты становятся аналогом конечного автомата:
Динамическое переключение сегментов создаёт псевдособытийную модель анимации.
После загрузки анимации доступна модификация внутренних свойств через
getElementById-подобные механизмы Lottie (через композицию
слоёв и data interfaces).
На практике чаще используется обход структуры:
const layers = animation.renderer.elements;
layers.forEach(layer => {
if (layer.data.nm === 'Circle') {
layer.updateDocumentData && layer.updateDocumentData({
text: 'dynamic'
});
}
});
Более устойчивый подход — работа через expressions в After Effects
или через dataInterface при экспорте.
Наиболее распространённый сценарий — изменение transform-параметров в runtime.
animation.addEventListener('enterFrame', () => {
const progress = animation.currentFrame / animation.totalFrames;
const scale = 100 + Math.sin(progress * Math.PI * 2) * 20;
animation.renderer.elements.forEach(el => {
if (el.transform) {
el.transform.scale.setValue([scale, scale]);
}
});
});
Такой подход превращает статическую анимацию в реактивную систему, где поведение определяется математическими функциями.
Процедурные анимации часто опираются на псевдослучайные значения для создания вариативности.
function randomRange(min, max) {
return Math.random() * (max - min) + min;
}
setInterval(() => {
const x = randomRange(-50, 50);
const y = randomRange(-50, 50);
animation.renderer.elements.forEach(el => {
if (el.transform && el.transform.position) {
el.transform.position.setValue([x, y]);
}
});
}, 1000);
Такой механизм используется для:
Процедурность в Lottie Web усиливается при связывании с состоянием интерфейса.
function updateState(state) {
switch(state) {
case 'loading':
animation.playSegments([0, 45], true);
break;
case 'success':
animation.playSegments([45, 90], true);
break;
case 'error':
animation.playSegments([90, 135], true);
break;
}
}
Таким образом анимация становится визуальным отражением бизнес-логики.
Более низкоуровневый способ процедурного контроля заключается в привязке к циклу рендера.
function loop() {
const t = performance.now() * 0.001;
const offset = Math.sin(t) * 10;
animation.renderer.elements.forEach(el => {
if (el.transform && el.transform.rotation) {
el.transform.rotation.setValue(offset);
}
});
requestAnimationFrame(loop);
}
loop();
Этот подход используется для создания непрерывных живых анимаций, не зависящих от таймлайна Lottie.
Часть процедурной логики переносится на этап экспорта через expressions:
Пример expression:
wiggle(2, 30)
После экспорта такие выражения сохраняют процедурное поведение внутри JSON, уменьшая необходимость JS-манипуляций.
Lottie позволяет подменять JSON-анимации на лету, что создаёт эффект генерации различных вариантов одной сцены.
animation.destroy();
lottie.loadAnimation({
container: document.getElementById('anim'),
renderer: 'svg',
loop: true,
autoplay: true,
path: dynamicPath
});
Этот механизм применяется для:
Процедурность усиливается при компоновке нескольких анимаций в одном контейнере.
const animA = lottie.loadAnimation({...});
const animB = lottie.loadAnimation({...});
function sync(progress) {
animA.goToAndStop(progress * 100, true);
animB.goToAndStop((1 - progress) * 100, true);
}
Такой подход формирует композитные визуальные системы, где отдельные Lottie-файлы становятся слоями общей сцены.
Имитация физики достигается через математические модели, накладываемые на параметры анимации:
let velocity = 0;
let position = 0;
function tick() {
const target = 100;
const stiffness = 0.1;
velocity += (target - position) * stiffness;
velocity *= 0.8;
position += velocity;
animation.renderer.elements.forEach(el => {
if (el.transform) {
el.transform.position.setValue([position, 0]);
}
});
requestAnimationFrame(tick);
}
tick();
Один Lottie-файл может использоваться как база для множества визуальных состояний:
fillfunction applyTheme(color) {
animation.renderer.elements.forEach(el => {
if (el.styles && el.styles.fill) {
el.styles.fill.setValue(color);
}
});
}
Глубокая модификация Lottie-структуры в runtime сопровождается ограничениями:
Поэтому процедурность чаще реализуется через управление временем и сегментами, а не через прямое изменение векторной геометрии.
Для поддержания производительности применяется:
requestAnimationFrame вместо
интерваловТакая архитектура позволяет сочетать генеративное поведение с предсказуемой нагрузкой на рендерер.