Lottie-анимации в Lottie Web представляют собой JSON-описания векторной анимации, которые интерпретируются в рантайме. Основная нагрузка при их использовании приходится не на рендеринг как таковой, а на этапы получения, парсинга и построения внутренней структуры композиции.
Ключевые узкие места:
Каждый из этих этапов может стать причиной задержки первого кадра, особенно при множественных анимациях на одной странице.
Предзагрузка в контексте Lottie Web сводится к разделению процесса инициализации на два этапа:
Основная идея заключается в том, чтобы завершить сетевую и парсинговую часть до момента, когда анимация реально становится видимой или активной.
Типовой поток:
lottie.loadAnimation по требованиюНаиболее прямолинейный способ предзагрузки — использование
fetch с последующим хранением результата в памяти.
const animationCache = new Map();
async function preloadAnimation(name, url) {
if (animationCache.has(name)) return animationCache.get(name);
const response = await fetch(url);
const json = await response.json();
animationCache.set(name, json);
return json;
}
В этом подходе основная оптимизация достигается за счёт исключения повторного сетевого запроса и повторного парсинга JSON.
При дальнейшем использовании данные передаются напрямую в Lottie:
import lottie fr om "lottie-web";
function mountAnimation(container, animationData) {
return lottie.loadAnimation({
container,
renderer: "svg",
loop: true,
autoplay: true,
animationData
});
}
Lottie Web поддерживает два основных режима инициализации:
path — загрузка JSON внутри библиотекиanimationData — передача уже готового объектаПредзагрузка эффективна только при использовании
animationData, так как исключает повторный fetch внутри
библиотеки.
Схема разделения:
// этап 1: подготовка
const data = await preloadAnimation("hero", "/anim/hero.json");
// этап 2: поздний рендер
lottie.loadAnimation({
container: document.getElementById("hero"),
renderer: "svg",
animationData: data
});
Одним из практических приёмов является инициализация анимации вне видимой области DOM. Это позволяет прогреть все внутренние структуры Lottie.
const hiddenContainer = document.createElement("div");
hiddenContainer.style.width = "0px";
hiddenContainer.style.height = "0px";
hiddenContainer.style.overflow = "hidden";
document.body.appendChild(hiddenContainer);
const anim = lottie.loadAnimation({
container: hiddenContainer,
renderer: "svg",
loop: false,
autoplay: false,
animationData: cachedData
});
anim.goToAndStop(0, true);
Такой подход инициирует:
После этого реальный рендер в видимом контейнере выполняется значительно быстрее, так как большая часть работы уже завершена.
Часто предзагрузка комбинируется с ленивой загрузкой, где данные начинают загружаться до появления элемента в viewport.
const observer = new IntersectionObserver(async (entries) => {
for (const entry of entries) {
if (!entry.isIntersecting) continue;
const el = entry.target;
const url = el.dataset.anim;
const data = await preloadAnimation(url, url);
lottie.loadAnimation({
container: el,
renderer: "svg",
animationData: data,
loop: true,
autoplay: true
});
observer.unobserve(el);
}
});
document.querySelectorAll(".lottie").forEach(el => {
observer.observe(el);
});
Преимущество этого подхода заключается в том, что загрузка начинается до фактического отображения элемента, но без преждевременного расхода ресурсов.
При большом количестве Lottie-анимаций возникает проблема конкурентных запросов и пиковой нагрузки на CPU при парсинге JSON.
Решение заключается в ограничении параллелизма:
async function preloadBatch(items, lim it = 3) {
const queue = [...items];
const results = [];
async function worker() {
while (queue.length) {
const item = queue.shift();
const data = await preloadAnimation(item.name, item.url);
results.push({ name: item.name, data });
}
}
const workers = Array.from({ length: limit }, worker);
await Promise.all(workers);
return results;
}
Такой подход снижает вероятность:
Предзагрузка не всегда должна реализовываться на уровне JavaScript. При корректных заголовках серверного ответа браузер самостоятельно решает задачу кэширования.
Рекомендуемые заголовки:
Cache-Control: public, max-age=31536000ETagLast-ModifiedВ этом случае повторная загрузка JSON не происходит даже без явного кеша в коде.
Однако Lottie Web всё равно выполняет парсинг JSON при каждом создании анимации, поэтому оптимизация должна включать и in-memory кеширование.
При большом количестве анимаций простой Map становится
недостаточным из-за роста памяти.
Применяется ограниченный кеш:
class LRUCache {
constructor(limit = 10) {
this.limit = limit;
this.map = new Map();
}
get(key) {
if (!this.map.has(key)) return null;
const value = this.map.get(key);
this.map.delete(key);
this.map.set(key, value);
return value;
}
set(key, value) {
if (this.map.has(key)) {
this.map.delete(key);
} else if (this.map.size >= this.limit) {
const firstKey = this.map.keys().next().value;
this.map.delete(firstKey);
}
this.map.set(key, value);
}
}
Это позволяет удерживать баланс между:
Lottie-композиции могут включать:
Особенно затратной является загрузка изображений при SVG-рендерере.
Для ускорения применяется явная предзагрузка ресурсов:
function preloadImage(src) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onl oad = resolve;
img.oner ror = reject;
img.src = src;
});
}
Если пути к ассетам известны заранее, их загрузка может быть выполнена до инициализации Lottie.
Первый кадр Lottie-анимации зависит от нескольких факторов:
Оптимизация достигается комбинацией:
fetchДополнительно важным фактором является выбор рендерера:
В крупных приложениях предзагрузка обычно инкапсулируется в единый слой управления.
class LottiePreloader {
constructor() {
this.cache = new Map();
}
async load(url) {
if (this.cache.has(url)) return this.cache.get(url);
const res = await fetch(url);
const data = await res.json();
this.cache.set(url, data);
return data;
}
mount(container, url) {
const data = this.cache.get(url);
if (!data) {
return this.load(url).then(d => this.mount(container, url));
}
return lottie.loadAnimation({
container,
renderer: "svg",
animationData: data,
autoplay: true,
loop: true
});
}
}
Такой слой позволяет:
Практически эффективная модель часто делится на уровни:
requestIdleCallbackrequestIdleCallback(() => {
preloadAnimation("secondary", "/anim/secondary.json");
});
Такой подход позволяет равномерно распределять нагрузку.
Размер Lottie-файла напрямую влияет на эффективность предзагрузки:
Оптимизация на этапе предзагрузки часто дополняется оптимизацией самих анимаций в After Effects через Bodymovin экспорт.
Эффективная система предзагрузки Lottie Web обычно строится на сочетании:
Такая архитектура минимизирует задержки первого кадра и снижает вероятность фризов при массовом отображении анимаций.