Загрузка из локального файла

Lottie Web использует JSON-описание анимации, экспортированное из After Effects через Bodymovin. При работе с локальными файлами основная задача заключается в корректном получении JSON-структуры, её проверке и передаче в рендерер без промежуточных серверных запросов.


Подготовка структуры проекта

Работа с локальной загрузкой предполагает наличие минимальной разметки и контейнера для рендера анимации.

<div id="lottie-container"></div>

<input type="file" id="lottie-file" accept="application/json" />

Контейнер служит целью для отрисовки, а input обеспечивает доступ к файловой системе браузера в рамках ограничений безопасности.

Инициализация экземпляра Lottie осуществляется через метод lottie.loadAnimation.

import lottie from "lottie-web";

const animationInstance = lottie.loadAnimation({
  container: document.getElementById("lottie-container"),
  renderer: "svg",
  loop: true,
  autoplay: false,
  animationData: null
});

Параметр animationData в данном случае остаётся пустым до момента загрузки локального файла.


Чтение локального JSON-файла

Доступ к локальному файлу реализуется через FileReader. Браузер предоставляет объект File, содержащий выбранный JSON.

const input = document.getElementById("lottie-file");

input.addEventListener("change", (event) => {
  const file = event.target.files[0];
  if (!file) return;

  const reader = new FileReader();

  reader.onl oad = (e) => {
    const text = e.target.result;
  };

  reader.readAsText(file);
});

Метод readAsText преобразует бинарное содержимое в строку, пригодную для дальнейшего парсинга.


Парсинг JSON и передача в Lottie

После получения текстового представления выполняется преобразование в объект.

let animationData;

try {
  animationData = JSON.parse(text);
} catch (error) {
  console.error("Некорректный JSON файл");
}

После успешного разбора структура передаётся в уже созданный экземпляр или используется для перезагрузки анимации.

animationInstance.destroy();

lottie.loadAnimation({
  container: document.getElementById("lottie-container"),
  renderer: "svg",
  loop: true,
  autoplay: true,
  animationData
});

Использование destroy() предотвращает наложение старого состояния на новый рендер.


Инициализация без предварительного экземпляра

Альтернативная схема подразумевает создание анимации только после загрузки файла.

let currentAnimation = null;

input.addEventListener("change", (event) => {
  const file = event.target.files[0];
  const reader = new FileReader();

  reader.onl oad = (e) => {
    const animationData = JSON.parse(e.target.result);

    if (currentAnimation) {
      currentAnimation.destroy();
    }

    currentAnimation = lottie.loadAnimation({
      container: document.getElementById("lottie-container"),
      renderer: "svg",
      loop: true,
      autoplay: true,
      animationData
    });
  };

  reader.readAsText(file);
});

Такой подход исключает необходимость хранения промежуточного состояния и упрощает управление жизненным циклом.


Валидация структуры Lottie JSON

Файл Lottie содержит строго определённые поля: v, fr, ip, op, layers, assets. Отсутствие ключевых элементов приводит к некорректному рендерингу.

Минимальная проверка может включать:

function isValidLottie(data) {
  return (
    data &&
    typeof data === "object" &&
    Array.isArray(data.layers) &&
    typeof data.fr === "number"
  );
}

Перед загрузкой выполняется проверка:

const animationData = JSON.parse(text);

if (!isValidLottie(animationData)) {
  console.error("Структура не соответствует Lottie формату");
}

Работа с assets и локальными путями

Внутри Lottie JSON часто содержится секция assets, где указаны изображения или композиции.

При локальной загрузке возникает ограничение: пути к изображениям не всегда доступны напрямую через файловую систему браузера.

Типовая структура:

{
  "assets": [
    {
      "id": "image_0",
      "w": 200,
      "h": 200,
      "u": "images/",
      "p": "img_0.png"
    }
  ]
}

Поле u и p формируют путь к ресурсу. При локальной загрузке корректная работа требует:

  • сохранения структуры папок рядом с JSON
  • либо предварительного преобразования путей
  • либо инлайна изображений в base64

При использовании File API изображения также могут загружаться отдельно через дополнительные input-элементы, с последующей подстановкой в assets.


Обновление анимации без пересоздания DOM

При частой смене файлов важна оптимизация перерисовки.

function updateAnimation(data) {
  animationInstance.stop();
  animationInstance.destroy();

  animationInstance = lottie.loadAnimation({
    container: document.getElementById("lottie-container"),
    renderer: "svg",
    loop: true,
    autoplay: true,
    animationData: data
  });
}

SVG-рендерер в Lottie создаёт DOM-дерево, поэтому повторное использование контейнера без очистки приводит к накоплению элементов.


Потоковая загрузка через ArrayBuffer

Помимо readAsText, используется readAsArrayBuffer, что полезно для контроля над кодировками.

reader.onl oad = (e) => {
  const uint8Array = new Uint8Array(e.target.result);
  const text = new TextDecoder("utf-8").decode(uint8Array);
  const animationData = JSON.parse(text);
};

Такой подход обеспечивает более стабильную обработку больших файлов.


Обработка ошибок загрузки

Ошибки могут возникать на нескольких уровнях:

  • некорректный JSON
  • отсутствие обязательных полей
  • повреждённая структура layers
  • несовместимая версия Bodymovin
reader.oner ror = () => {
  console.error("Ошибка чтения файла");
};

try {
  const data = JSON.parse(text);
} catch (e) {
  console.error("Ошибка парсинга JSON");
}

Дополнительно проверяется версия:

if (data.v && parseFloat(data.v) > 5) {
  console.warn("Версия Lottie может быть несовместима");
}

Интеграция с множественными файлами

При работе с несколькими анимациями применяется массив файлов:

input.addEventListener("change", (event) => {
  const files = Array.from(event.target.files);

  files.forEach((file) => {
    const reader = new FileReader();

    reader.onl oad = (e) => {
      const data = JSON.parse(e.target.result);

      lottie.loadAnimation({
        container: document.createElement("div"),
        renderer: "svg",
        loop: true,
        autoplay: true,
        animationData: data
      });
    };

    reader.readAsText(file);
  });
});

Каждый файл создаёт независимый экземпляр анимации, что требует контроля ресурсов при большом количестве загрузок.


Поведение при повторной загрузке одного файла

Браузер может не триггерить change при выборе того же файла повторно. Решение заключается в сбросе значения input:

input.value = "";

Сброс позволяет повторно инициировать событие выбора даже при идентичном файле.


Оптимизация производительности при локальной загрузке

При работе с крупными JSON-файлами применяются следующие принципы:

  • минимизация пересоздания DOM
  • использование requestAnimationFrame для синхронизации обновлений
  • отключение autoplay до полной загрузки
  • предварительная проверка структуры до передачи в Lottie

Рендер SVG может создавать значительную нагрузку при сложных композициях, поэтому WebGL-рендерер иногда используется как альтернатива:

renderer: "canvas"

или

renderer: "svg"

Синхронизация загрузки и управления состоянием

При асинхронной загрузке важно разделять этапы:

  • получение файла
  • чтение содержимого
  • парсинг JSON
  • валидация структуры
  • инициализация рендера

Каждый этап может быть изолирован:

async function loadLottie(file) {
  const text = await file.text();
  const data = JSON.parse(text);

  if (!isValidLottie(data)) return;

  return lottie.loadAnimation({
    container: document.getElementById("lottie-container"),
    renderer: "svg",
    loop: true,
    autoplay: true,
    animationData: data
  });
}

Такой подход уменьшает связность логики и упрощает контроль ошибок на каждом этапе.