В браузерной среде каждая анимация Lottie, созданная через
lottie-web, представляет собой независимый экземпляр с
собственным состоянием, DOM-привязкой и жизненным циклом. При
масштабировании интерфейсов количество таких экземпляров быстро
возрастает: списки карточек, динамические ленты, модальные окна, иконки
состояний, интерактивные графические элементы.
Основная точка входа — функция lottie.loadAnimation,
возвращающая объект управления анимацией. Каждый вызов формирует
изолированную сущность, не связанную автоматически с другими
экземплярами.
import lottie from "lottie-web";
const animation = lottie.loadAnimation({
container: document.getElementById("anim"),
renderer: "svg",
loop: true,
autoplay: true,
path: "/animations/data.json"
});
Каждый такой объект содержит внутренние ресурсы: SVG/Canvas/HTML-структуру, таймеры, обработчики и данные текущего состояния проигрывания.
При наличии нескольких анимаций требуется централизованное хранение ссылок на экземпляры. Это позволяет управлять их жизненным циклом без потери контроля над ранее созданными объектами.
На практике используется структура Map, где ключом
выступает идентификатор DOM-узла, бизнес-ключ или индекс:
const animations = new Map();
function createAnimation(id, container, dataPath) {
const instance = lottie.loadAnimation({
container,
renderer: "svg",
loop: true,
autoplay: true,
path: dataPath
});
animations.set(id, instance);
return instance;
}
Такой подход обеспечивает предсказуемый доступ к любому экземпляру без необходимости обхода DOM.
Каждая анимация Lottie проходит одинаковые стадии:
Критически важным этапом является явное завершение работы экземпляра.
function destroyAnimation(id) {
const instance = animations.get(id);
if (instance) {
instance.destroy();
animations.delete(id);
}
}
Метод destroy() освобождает DOM-структуры, снимает
обработчики и очищает внутренние ссылки. Отсутствие вызова приводит к
накоплению ресурсов и постепенному падению производительности.
В ряде сценариев создаётся необходимость замены анимации без пересоздания контейнера. Подход зависит от версии и конфигурации рендера.
Базовая модель предполагает уничтожение и пересоздание:
function replaceAnimation(id, newPath) {
const oldInstance = animations.get(id);
if (oldInstance) {
oldInstance.destroy();
}
const container = document.getElementById(id);
const newInstance = lottie.loadAnimation({
container,
renderer: "svg",
loop: true,
autoplay: true,
path: newPath
});
animations.set(id, newInstance);
}
Такой механизм сохраняет консистентность состояния, избегая конфликтов между слоями SVG.
При наличии множественных анимаций часто требуется групповое управление состоянием: пауза, воспроизведение, изменение скорости.
function pauseAll() {
animations.forEach((instance) => {
instance.pause();
});
}
function playAll() {
animations.forEach((instance) => {
instance.play();
});
}
function setSpeedAll(speed) {
animations.forEach((instance) => {
instance.setSpeed(speed);
});
}
Такой слой абстракции превращает набор независимых объектов в управляемую систему.
В сложных интерфейсах требуется синхронное поведение нескольких анимаций. Это касается:
Контроль осуществляется через единый источник состояния:
let globalSpeed = 1;
function syncSpeed(speed) {
globalSpeed = speed;
animations.forEach((instance) => {
instance.setSpeed(globalSpeed);
});
}
При необходимости синхронизации прогресса используется метод
goToAndStop:
function syncProgress(frame) {
animations.forEach((instance) => {
instance.goToAndStop(frame, true);
});
}
DOM-структура контейнеров напрямую влияет на корректность работы экземпляров. Перемещение элементов без пересоздания анимации часто приводит к нарушению рендеринга, особенно при SVG-режиме.
При изменении DOM-структуры применяется пересборка:
function moveAnimation(id, newContainer) {
const instance = animations.get(id);
if (!instance) return;
instance.destroy();
const newInstance = lottie.loadAnimation({
container: newContainer,
renderer: "svg",
loop: true,
autoplay: true,
animationData: instance.animationData
});
animations.set(id, newInstance);
}
Контейнер Lottie строго привязан к DOM-узлу. Один контейнер не предназначен для одновременного использования несколькими экземплярами.
При переиспользовании применяется стратегия очистки содержимого:
function clearContainer(container) {
while (container.firstChild) {
container.removeChild(container.firstChild);
}
}
Далее создаётся новый экземпляр поверх очищенного узла.
В интерфейсах с динамическими списками количество анимаций постоянно изменяется. При добавлении элементов создаются новые экземпляры, при удалении — уничтожаются.
function renderList(items) {
items.forEach((item) => {
const container = document.createElement("div");
container.id = item.id;
document.body.appendChild(container);
createAnimation(item.id, container, item.path);
});
}
function removeItem(id) {
destroyAnimation(id);
const el = document.getElementById(id);
if (el) el.remove();
}
Ключевым аспектом является строгая синхронизация DOM и реестра экземпляров.
При увеличении числа анимаций нагрузка на рендеринг возрастает экспоненциально, особенно в SVG-режиме. Основной фактор — количество одновременно активных DOM-узлов и вычислений кадров.
Типовой подход включает ограничение активных экземпляров:
const MAX_ACTIVE = 20;
function limitInstances() {
if (animations.size <= MAX_ACTIVE) return;
const firstKey = animations.keys().next().value;
destroyAnimation(firstKey);
}
Дополнительно применяется приостановка невидимых элементов:
function pauseInvisible(entries) {
entries.forEach((entry) => {
const id = entry.target.id;
const instance = animations.get(id);
if (!instance) return;
if (entry.isIntersecting) {
instance.play();
} else {
instance.pause();
}
});
}
В SPA-подобных архитектурах экземпляры Lottie должны реагировать на смену экранов, маршрутов и модальных состояний.
При переходе между состояниями интерфейса применяется массовая очистка:
function destroyAll() {
animations.forEach((instance) => {
instance.destroy();
});
animations.clear();
}
Такой механизм предотвращает утечки памяти при смене контекста приложения.
При усложнении системы применяется фабричный слой, инкапсулирующий создание и контроль:
function animationFactory() {
const store = new Map();
return {
create(id, container, path) {
const instance = lottie.loadAnimation({
container,
renderer: "svg",
loop: true,
autoplay: true,
path
});
store.set(id, instance);
},
get(id) {
return store.get(id);
},
destroy(id) {
const instance = store.get(id);
if (instance) {
instance.destroy();
store.delete(id);
}
},
destroyAll() {
store.forEach((instance) => instance.destroy());
store.clear();
}
};
}
Такой слой отделяет бизнес-логику от низкоуровневого управления экземплярами.
Повторный вызов lottie.loadAnimation на одном и том же
контейнере без предварительного уничтожения предыдущего экземпляра
приводит к наложению SVG-слоёв и неконтролируемому росту DOM.
Защита реализуется через проверку:
function safeCreate(id, container, path) {
if (animations.has(id)) {
destroyAnimation(id);
}
return createAnimation(id, container, path);
}
Основные источники утечек:
destroy()MapКонтроль осуществляется через строгую дисциплину удаления экземпляров при любом изменении DOM-структуры.
window.addEventListener("beforeunload", () => {
animations.forEach((instance) => instance.destroy());
});