Изменение размеров в runtime

Изменение размеров Lottie-анимаций в runtime в Lottie Web строится вокруг взаимодействия контейнера, параметров рендера и внутренних механизмов пересчёта сцены. Библиотека Lottie Web не пересчитывает геометрию автоматически во всех сценариях, поэтому корректное масштабирование зависит от выбранного рендерера и способа привязки размеров к DOM.

Lottie-анимация привязывается к контейнеру, переданному в loadAnimation. Размер сцены определяется не только параметрами JSON, но и фактическими размерами DOM-элемента.

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

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

Рендереры и их влияние на изменение размеров

SVG-рендерер

SVG масштабируется через viewBox. В большинстве случаев изменение CSS-размеров контейнера приводит к автоматическому масштабированию без перерасчёта кадров.

Ключевая особенность — зависимость от preserveAspectRatio.

rendererSettings: {
  preserveAspectRatio: 'xMidYMid meet'
}

Поведение:

  • meet — сохранение пропорций с возможными полями
  • slice — заполнение контейнера с обрезкой
  • none — растяжение без сохранения пропорций

SVG чаще всего не требует явного вызова пересчёта, но исключения возникают при динамическом изменении размеров родителя без изменения layout-событий.


Canvas-рендерер

Canvas требует явного пересчёта размеров. При изменении контейнера происходит изменение внутренних буферов отрисовки.

const animation = lottie.loadAnimation({
  container: document.getElementById('anim'),
  renderer: 'canvas',
  path: '/animation.json'
});

При изменении размеров контейнера необходимо синхронизировать canvas:

animation.resize();

Без этого кадр остаётся в старом разрешении, что приводит к размытой или некорректной отрисовке.


HTML-рендерер

HTML-рендерер использует DOM-элементы для каждого слоя. Масштабирование зависит от CSS и трансформаций transform: scale().

Особенность — высокая нагрузка при большом количестве слоёв и необходимость пересчёта позиционирования при resize.


Механизмы отслеживания изменения размеров

window resize

Классический способ отслеживания изменений viewport:

window.addEventListener('resize', () => {
  animation.resize();
});

Этот подход работает, но не учитывает изменения размеров контейнеров внутри сложных layout-систем.


ResizeObserver

Более точный механизм, реагирующий на изменение конкретного элемента:

const container = document.getElementById('anim');

const observer = new ResizeObserver(() => {
  animation.resize();
});

observer.observe(container);

Использование ResizeObserver устраняет необходимость реагировать на глобальные события окна.


Debounce при частых изменениях

При изменении размеров через анимации layout или drag-операции возникает частое срабатывание resize. Это приводит к лишним перерасчётам.

function debounce(fn, delay) {
  let t;
  return (...args) => {
    clearTimeout(t);
    t = setTimeout(() => fn(...args), delay);
  };
}

const onRes ize = debounce(() => animation.resize(), 100);

window.addEventListener('resize', onResize);

Метод resize и внутренние процессы

resize() инициирует перерасчёт viewport и пересборку матриц трансформации.

Внутренне происходит:

  • пересчёт размеров контейнера
  • обновление scale-фактора
  • перерасчёт bounding boxes слоёв
  • обновление canvas buffer (для canvas renderer)
  • пересоздание SVG трансформаций (при необходимости)

Частый вызов приводит к затратам CPU, особенно при сложных композициях.


DPI и плотность пикселей

Для canvas важно учитывать devicePixelRatio.

Без учёта DPR изображение становится размытым на дисплеях высокой плотности.

Типичная корректировка:

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');

const dpr = window.devicePixelRatio || 1;
canvas.width = container.clientWidth * dpr;
canvas.height = container.clientHeight * dpr;

ctx.scale(dpr, dpr);

В Lottie Web часть этой логики обрабатывается автоматически, но при кастомных интеграциях возможны расхождения.


CSS-стратегии для адаптивного поведения

Часто изменение размеров полностью делегируется CSS:

#anim {
  width: 100%;
  height: 100%;
}

При таком подходе Lottie зависит от layout-системы страницы.

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

#anim {
  position: absolute;
  inset: 0;
}

Это фиксирует контейнер в пределах родителя и упрощает масштабирование.


Пересоздание анимации вместо resize

В сложных сценариях изменение размеров может сопровождаться пересборкой animation instance:

animation.destroy();

lottie.loadAnimation({
  container,
  renderer: 'svg',
  path: '/animation.json'
});

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

  • рендерера
  • JSON конфигурации
  • режима aspect ratio

Пересоздание дороже, чем resize, но обеспечивает предсказуемость.


Особенности при SPA и виртуальном DOM

В SPA-фреймворках контейнер может быть не готов в момент инициализации.

Типичная проблема — нулевые размеры:

if (container.clientWidth === 0) {
  requestAnimationFrame(init);
}

Также важен повторный вызов resize() после навигации между страницами, так как DOM может быть повторно смонтирован с другими размерами.


Проблемы синхронизации размеров

Распространённые ситуации:

  • контейнер изменился, но resize не вызван
  • SVG масштабируется, canvas остаётся фиксированным
  • асинхронная загрузка layout вызывает «скачки»
  • отсутствие debounce приводит к лагам

Для стабильного поведения используется комбинация:

  • ResizeObserver
  • animation.resize()
  • CSS 100% sizing
  • минимизация пересоздания инстанса

Масштабирование внутри flex/grid layout

В современных layout-системах размеры контейнера часто изменяются без событий window.

Особенно в:

  • flex-grow контейнерах
  • grid auto-fill областях
  • split-pane интерфейсах

В этих случаях только ResizeObserver отражает фактическое изменение геометрии.


Поведение при скрытии контейнера

Если контейнер получает display: none, размеры становятся нулевыми. После повторного отображения требуется повторный resize:

animation.resize();

Без этого возможны артефакты отрисовки или отсутствие кадров.


Оптимизация частоты перерасчётов

При высокой частоте изменений размеров применяется стратегия:

  • объединение событий
  • throttling через requestAnimationFrame
  • отложенный resize
let scheduled = false;

function scheduleResize() {
  if (scheduled) return;
  scheduled = true;

  requestAnimationFrame(() => {
    animation.resize();
    scheduled = false;
  });
}

Поведение при изменении ориентации экрана

На мобильных устройствах изменение orientation вызывает резкое изменение viewport.

window.addEventListener('orientationchange', () => {
  setTimeout(() => animation.resize(), 200);
});

Задержка необходима для завершения перерасчёта layout браузером.


Сочетание с динамическими контейнерами

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

Типичный сценарий:

  • контейнер скрыт → размеры 0
  • контейнер открыт → размеры изменены
  • требуется resize() после открытия

Итоговые паттерны поведения

Устойчивые схемы работы с изменением размеров включают:

  • привязку к ResizeObserver
  • минимизацию вызовов resize()
  • использование CSS 100% ширины и высоты
  • контроль preserveAspectRatio
  • учёт различий SVG и Canvas рендереров