Динамическое изменение текста

Динамический текст в Lottie Web строится вокруг работы с текстовыми слоями, которые в экспортированном JSON из After Effects представляют собой структуру данных, описывающую содержимое, стили и параметры рендеринга. Каждый текстовый элемент хранится внутри слоя типа "t" и содержит вложенную иерархию, где ключевым является поле t внутри массива k, отвечающее за сам текстовый контент.


Текстовый слой в Lottie представляется следующим образом:

{
  "ty": 5,
  "t": {
    "d": {
      "k": [
        {
          "s": {
            "t": "Hello World"
          }
        }
      ]
    }
  }
}

Ключевые элементы:

  • ty: 5 — обозначение текстового слоя
  • t.d.k[] — массив ключей анимации текста
  • s.t — строка текста, отображаемая в анимации

Даже при отсутствии анимации текста структура сохраняется, поскольку Lottie рассматривает текст как анимируемый параметр.


Доступ к текстовым слоям через Lottie Web

После инициализации анимации доступ к данным возможен через объект экземпляра:

const anim = lottie.loadAnimation({
  container: document.getElementById('container'),
  renderer: 'svg',
  loop: true,
  autoplay: true,
  path: 'animation.json'
});

Экземпляр содержит внутренние структуры:

  • anim.renderer
  • anim.renderer.elements
  • anim.data.layers

Наиболее устойчивый способ модификации текста — работа через данные слоя и метод обновления документа.


Модификация текста через updateDocumentData

Одним из поддерживаемых механизмов является обновление текстовых данных через updateDocumentData, доступный в части сборок Lottie Web.

anim.updateDocumentData({
  text: 'Новый текст'
});

Однако в реальных проектах поведение зависит от версии сборки и типа рендера (SVG, Canvas, HTML).

Чаще применяется точечное обновление слоя:

anim.addEventListener('DOMLoaded', () => {
  const layers = anim.renderer.elements;

  layers.forEach(layer => {
    if (layer.data && layer.data.ty === 5) {
      layer.updateDocumentData({
        t: 'Новый текст'
      });
    }
  });
});

Изменение текста через структуру слоёв

Внутреннее представление позволяет напрямую модифицировать значение:

anim.addEventListener('DOMLoaded', () => {
  const layers = anim.renderer.elements;

  for (let i = 0; i < layers.length; i++) {
    const layer = layers[i];

    if (layer.data && layer.data.ty === 5) {
      const textProperty = layer.data.t.d.k[0].s;

      textProperty.t = 'Обновлённый текст';
    }
  }

  anim.renderer.renderFrame(true);
});

Важные аспекты:

  • изменение происходит в памяти объекта
  • требуется перерисовка кадра
  • структура k[0] соответствует первому ключу анимации текста

Работа с множественными ключами анимации текста

Если текст анимирован во времени, структура содержит несколько ключей:

"k": [
  { "t": 0, "s": { "t": "Первый текст" } },
  { "t": 50, "s": { "t": "Второй текст" } }
]

Модификация всех ключей:

layers.forEach(layer => {
  if (layer.data.ty === 5) {
    const keys = layer.data.t.d.k;

    for (let i = 0; i < keys.length; i++) {
      keys[i].s.t = 'Единый текст для всех ключей';
    }
  }
});

Такой подход выравнивает текстовую анимацию, заменяя все состояния одним значением.


Обновление через setSubframe и перерендеринг

После изменения данных требуется обновление кадра:

anim.setSubframe(false);
anim.goToAndStop(anim.currentFrame, true);

или принудительная отрисовка:

anim.renderer.renderFrame(true);

Эти методы влияют на синхронизацию визуального состояния с изменёнными данными.


Манипуляция через элементы рендера SVG

При использовании SVG-рендера текст часто представлен DOM-элементами <text>:

const svgTextElements = document.querySelectorAll('svg text');

svgTextElements.forEach(el => {
  el.textContent = 'Изменённый текст';
});

Однако такой подход:

  • нарушает реактивность Lottie
  • может быть перезаписан при следующем кадре
  • не синхронизируется с внутренним состоянием анимации

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


Использование data-driven подмены текста

Распространённый подход основан на изменении исходных данных перед загрузкой:

const animationData = fetch('animation.json')
  .then(res => res.json())
  .then(data => {
    data.layers.forEach(layer => {
      if (layer.ty === 5) {
        layer.t.d.k.forEach(k => {
          k.s.t = 'Динамический текст';
        });
      }
    });

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

Такой метод обеспечивает:

  • стабильную отрисовку
  • отсутствие конфликтов с рендерером
  • контроль над всеми текстовыми состояниями до запуска

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

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

function updateLottieText(anim, value) {
  const layers = anim.renderer.elements;

  for (let i = 0; i < layers.length; i++) {
    const layer = layers[i];

    if (layer.data.ty === 5) {
      const keys = layer.data.t.d.k;

      for (let j = 0; j < keys.length; j++) {
        keys[j].s.t = value;
      }
    }
  }

  anim.renderer.renderFrame(true);
}

Особенности поведения:

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

Ограничения динамического текста

При работе с текстовыми слоями Lottie Web проявляются системные ограничения:

  • отсутствие официального стабильного публичного API для текстовых изменений в рантайме во всех версиях
  • зависимость от рендерера (SVG / Canvas / HTML)
  • возможная потеря изменений при пересчёте композиции
  • различия в структуре JSON между версиями Bodymovin

Поведение при кэшировании слоёв

Lottie оптимизирует рендеринг, кэшируя элементы:

  • текстовые блоки могут храниться как подготовленные DOM-узлы
  • изменение data не всегда приводит к немедленному обновлению
  • требуется принудительная инвалидация кадра
anim.renderer.destroyed = false;
anim.renderer.renderFrame(true);

Динамическая подмена текста в сложных композициях

В композициях с несколькими сценами текстовые слои могут быть вложены в прекомпозиции:

function traverseLayers(layers) {
  for (let i = 0; i < layers.length; i++) {
    const layer = layers[i];

    if (layer.ty === 5) {
      layer.t.d.k.forEach(k => {
        k.s.t = 'Обновлённый контент';
      });
    }

    if (layer.layers) {
      traverseLayers(layer.layers);
    }
  }
}

Такой обход необходим при сложной структуре экспортов из After Effects с nested compositions.


Реактивное обновление текста без перезагрузки анимации

Изменение текста без повторной инициализации анимации достигается через прямую модификацию data-структуры:

function setText(anim, newText) {
  const layers = anim.renderer.elements;

  layers.forEach(layer => {
    if (layer.data && layer.data.ty === 5) {
      layer.data.t.d.k[0].s.t = newText;
    }
  });

  anim.renderer.renderFrame(true);
}

Это позволяет сохранять текущее состояние анимации, изменяя только текстовую часть.


Поведение при разных рендерах

SVG

  • текст — DOM элементы
  • высокая гибкость модификации
  • возможны перерисовки DOM

Canvas

  • текст рендерится в bitmap
  • изменение требует полного перерендеринга кадра

HTML renderer

  • текст представлен HTML-элементами
  • возможна стилизация через CSS и DOM-манипуляции

Управление текстом через внешние данные

При архитектуре с внешним источником данных текст часто связывается с API:

fetch('/api/title')
  .then(res => res.json())
  .then(data => {
    updateLottieText(anim, data.title);
  });

Такой подход сохраняет разделение логики:

  • Lottie отвечает за визуализацию
  • внешняя система управляет содержанием текста