Lottie-анимации строятся на структуре JSON, где каждый визуальный
элемент описывается как набор слоёв, фигур и свойств. Цвета в этой
структуре не являются «картинкой», они хранятся как числовые значения в
объектах fill и stroke, что делает возможным
их изменение как на этапе подготовки JSON, так и во время выполнения в
браузере через Lottie Web.
Цвет в Lottie задаётся в нормализованном виде — массивом RGB-компонентов от 0 до 1:
"c": {
"a": 0,
"k": [1, 0, 0, 1]
}
Где:
k — ключевое значение цвета[r, g, b, a]0..1Красный цвет:
[1, 0, 0]
Чёрный:
[0, 0, 0]
Белый:
[1, 1, 1]
Цвета применяются внутри объектов:
fl — fill (заливка)st — stroke (обводка)Пример слоя формы:
{
"ty": "fl",
"c": {
"k": [0.2, 0.6, 1]
}
}
Lottie Web предоставляет API:
const animation = lottie.loadAnimation({
container: document.getElementById('app'),
renderer: 'svg',
loop: true,
autoplay: true,
path: 'animation.json'
});
Ключевой момент: способ изменения цвета зависит от renderer.
Наиболее стабильный и предсказуемый способ — изменение JSON перед
передачей в loadAnimation.
fetch('animation.json')
.then(res => res.json())
.then(data => {
data.layers.forEach(layer => {
traverseShapes(layer);
});
lottie.loadAnimation({
container: document.getElementById('app'),
renderer: 'svg',
loop: true,
autoplay: true,
animationData: data
});
});
Рекурсивный обход структуры:
function traverseShapes(node) {
if (!node || typeof node !== 'object') return;
if (node.ty === 'fl' && node.c) {
node.c.k = [0, 1, 0]; // перекраска в зелёный
}
if (node.ty === 'st' && node.c) {
node.c.k = [1, 0, 0]; // красная обводка
}
if (node.it) {
node.it.forEach(traverseShapes);
}
if (node.layers) {
node.layers.forEach(traverseShapes);
}
}
Этот метод:
При использовании:
renderer: 'svg'
Lottie создаёт DOM-структуру внутри контейнера. Это позволяет
напрямую менять fill и stroke.
animation.addEventListener('DOMLoaded', () => {
const svg = document.querySelector('#app svg');
const paths = svg.querySelectorAll('path');
paths.forEach(path => {
path.setAttribute('fill', '#00ff00');
});
});
Однако важно понимать ограничение: структура SVG может быть сложной,
и не все элементы являются path.
Часто используются:
pathgcirclerectБолее точечный подход:
const groups = svg.querySelectorAll('g');
groups.forEach(group => {
if (group.getAttribute('data-name') === 'Primary') {
const shapes = group.querySelectorAll('*');
shapes.forEach(el => {
if (el.getAttribute('fill')) {
el.setAttribute('fill', '#ff0000');
}
});
}
});
Lottie позволяет реагировать на кадры:
animation.addEventListener('enterFrame', () => {
const progress = animation.currentFrame / animation.totalFrames;
if (progress > 0.5) {
const svg = document.querySelector('#app svg');
svg.querySelectorAll('path').forEach(p => {
p.setAttribute('fill', '#0000ff');
});
}
});
Это создаёт эффект изменения палитры в зависимости от времени анимации.
Lottie Web не предоставляет полноценного API для изменения свойств слоёв на уровне композиции. Поэтому важно учитывать:
setColor() для shape layersДля глобального изменения цветовой схемы можно использовать CSS-фильтры:
#app svg {
filter: hue-rotate(90deg) saturate(1.2);
}
Этот метод:
При экспорте из After Effects через Bodymovin можно задавать имена слоёв:
PrimaryColorAccentBackgroundВ JSON они попадают как:
"nm": "PrimaryColor"
Далее можно использовать поиск:
function findLayersByName(data, name) {
const result = [];
function walk(node) {
if (node.nm === name) result.push(node);
if (node.layers) node.layers.forEach(walk);
if (node.it) node.it.forEach(walk);
}
walk(data);
return result;
}
И менять цвет выборочно:
const targets = findLayersByName(animationData, 'PrimaryColor');
targets.forEach(layer => {
traverseShapes(layer);
});
Подход с палитрой используется для унификации темы:
const palette = {
primary: [0.1, 0.4, 1],
secondary: [1, 0.5, 0],
background: [0.05, 0.05, 0.05]
};
Применение:
function applyPalette(node, palette) {
if (!node) return;
if (node.ty === 'fl') {
node.c.k = palette.primary;
}
if (node.ty === 'st') {
node.c.k = palette.secondary;
}
if (node.it) {
node.it.forEach(n => applyPalette(n, palette));
}
}
Такой подход удобен для:
В реальных проектах используется гибридный подход:
Пример архитектуры:
const animationData = await loadJSON();
applyPalette(animationData, themePalette);
const anim = lottie.loadAnimation({
container: el,
renderer: 'svg',
animationData
});
anim.addEventListener('DOMLoaded', () => {
highlightAccentLayers(el);
});
Цвета могут быть не только fl и st, но и
градиентами:
"ty": "gf"
Градиенты содержат массив цветов:
"g": {
"k": {
"k": [0, 0, 1, 1, 0, 1, 0, 1]
}
}
Где пары значений — это RGB + позиция.
Изменение требует перерасчёта всего массива:
function replaceGradient(node, from, to) {
if (node.ty === 'gf' && node.g?.k?.k) {
node.g.k.k = node.g.k.k.map(v => v === from ? to : v);
}
if (node.it) {
node.it.forEach(n => replaceGradient(n, from, to));
}
}
Изменение цветов в Lottie Web всегда сводится к работе с одним из трёх уровней:
Каждый из них имеет собственные ограничения по точности, производительности и гибкости, и выбор подхода определяется тем, требуется ли статическая кастомизация, динамическое переключение темы или визуальные эффекты во время проигрывания анимации.