Работа с разными разрешениями экрана

Анимации Lottie основаны на векторной графике, что делает их изначально независимыми от разрешения экрана. Однако поведение в браузере зависит от выбранного рендера, контейнера и механизма масштабирования. Ключевая задача — согласовать внутреннюю систему координат анимации с реальными размерами DOM-элемента и плотностью пикселей устройства.

Базовая единица измерения Lottie — логическое пространство композиции, заданное в After Effects. При рендере это пространство проецируется либо в SVG, либо в Canvas, и именно на этом этапе возникают различия в масштабировании, резкости и производительности.


Учет devicePixelRatio и влияние плотности пикселей

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

  • размытые линии на Retina-дисплеях
  • несоответствие геометрии при масштабировании Canvas
  • визуальные расхождения между SVG и растровым рендером

При использовании Canvas-рендера важно учитывать увеличение внутреннего буфера:

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;

canvas.style.width = `${container.clientWidth}px`;
canvas.style.height = `${container.clientHeight}px`;

ctx.scale(dpr, dpr);

SVG-рендер в большинстве случаев обрабатывает плотность пикселей автоматически, однако визуальная четкость зависит от корректной работы viewBox и preserveAspectRatio.


SVG и Canvas: различия масштабирования

SVG-рендер

SVG обеспечивает автоматическое масштабирование через viewBox. Основные характеристики:

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

Механизм масштабирования базируется на атрибутах контейнера:

lottie.loadAnimation({
  container: element,
  renderer: 'svg',
  loop: true,
  autoplay: true,
  path: 'animation.json',
  rendererSettings: {
    preserveAspectRatio: 'xMidYMid meet'
  }
});

preserveAspectRatio определяет поведение при несоответствии пропорций контейнера и композиции. Значения meet и slice задают стратегию вписывания или обрезки.


Canvas-рендер

Canvas требует ручного управления масштабом. Основные особенности:

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

Canvas требует синхронизации размеров буфера и CSS-отображения. Несоответствие этих значений приводит к размытию.


Контейнерная модель и CSS-стратегии

Корректное поведение Lottie-анимаций зависит от размеров родительского контейнера. Основные подходы:

Фиксация через flexbox

.container {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
}

Использование aspect-ratio

.lottie-box {
  width: 100%;
  aspect-ratio: 16 / 9;
}

Абсолютное позиционирование

.lottie-wrapper {
  position: absolute;
  inset: 0;
}

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


Обработка изменения размеров окна

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

Типовой подход:

let animation = lottie.loadAnimation({
  container: element,
  renderer: 'svg',
  loop: true,
  autoplay: true,
  path: 'animation.json'
});

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

Метод resize() инициирует перерасчет размеров SVG или Canvas, однако поведение зависит от рендера и версии библиотеки.


ResizeObserver как основной механизм синхронизации

Использование window.resize ограничено глобальными изменениями окна. В компонентных интерфейсах контейнер может изменяться независимо, что делает более точным ResizeObserver.

const observer = new ResizeObserver(entries => {
  for (const entry of entries) {
    const { width, height } = entry.contentRect;
    animation.resize();
  }
});

observer.observe(container);

Этот механизм обеспечивает реакцию на любые изменения макета: скрытие панелей, изменение flex-структуры, динамическую загрузку контента.


Пересоздание анимации и динамическое обновление размеров

В некоторых сценариях вызов resize() недостаточен. Это характерно для:

  • смены renderer (SVG ↔︎ Canvas)
  • изменения композиции
  • переключения темы с изменением масштабов
  • динамической подгрузки разных JSON

В таких случаях используется пересоздание экземпляра:

animation.destroy();

animation = lottie.loadAnimation({
  container: element,
  renderer: 'svg',
  loop: true,
  autoplay: true,
  path: 'new-animation.json'
});

Пересоздание обеспечивает корректную пересборку внутренней структуры сцен.


Поведение preserveAspectRatio и система координат

SVG-рендер Lottie опирается на систему viewBox, аналогичную SVG-спецификации. Параметр preserveAspectRatio управляет тем, как композиция вписывается в контейнер.

Основные режимы:

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

Несоответствие этого параметра приводит к смещению или деформации анимации при изменении размеров контейнера.


Особенности масштабирования на мобильных устройствах

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

  • динамическое изменение viewport при скрытии адресной строки
  • высокая плотность пикселей (2x, 3x)
  • ограниченные ресурсы CPU/GPU

Рекомендованные подходы:

  • фиксирование размеров через vh и dvh
  • использование ResizeObserver вместо resize
  • отключение сложных эффектов при низкой производительности
.mobile-container {
  height: 100dvh;
  width: 100%;
}

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

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

let frame;

function onResize() {
  cancelAnimationFrame(frame);
  frame = requestAnimationFrame(() => {
    animation.resize();
  });
}

window.addEventListener('resize', onResize);

Дополнительно применяется разделение логики:

  • вычисление размеров DOM
  • обновление canvas/SVG
  • синхронизация анимационного цикла

Canvas-рендер и точная подгонка под размеры контейнера

При Canvas-рендере важна синхронизация трех параметров:

  • canvas.width
  • canvas.height
  • CSS-ширина и высота

Несоответствие приводит к интерполяции и размытию изображения. Корректная модель масштабирования включает:

  • умножение на DPR
  • масштабирование контекста
  • фиксацию CSS-слоя

Стабилизация рендера при динамическом layout

При работе в SPA-интерфейсах размеры контейнера могут изменяться каскадно. Основные проблемы:

  • промежуточные нулевые размеры при скрытии блоков
  • повторные перерасчеты layout
  • дергание анимации при частых пересчетах

Решение заключается в проверке валидности размеров перед обновлением:

function safeResize() {
  const rect = container.getBoundingClientRect();

  if (rect.width === 0 || rect.height === 0) return;

  animation.resize();
}

Согласование Lottie с responsive layout системами

В системах с grid и flex важно учитывать, что Lottie не управляет размерами самостоятельно. Поведение полностью зависит от родителя.

Типовые модели:

  • фиксированная сетка (grid-template)
  • адаптивные колонки (minmax)
  • процентные размеры

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

.layout {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

Lottie-анимации в таком случае масштабируются вместе с ячейкой сетки, сохраняя пропорции при корректной настройке рендера.


Управление масштабом через API Lottie

Дополнительно к системному масштабированию используется API управления:

  • animation.setSpeed()
  • animation.resize()
  • animation.goToAndPlay()

Хотя прямого метода setScale() нет, масштабирование достигается через контейнер и rendererSettings.


Особенности повторного использования экземпляров

При повторном использовании контейнера важно учитывать:

  • необходимость очистки DOM
  • удаление canvas-слоев
  • сброс внутренних слушателей событий
animation.destroy();
container.innerHTML = '';

Игнорирование очистки приводит к накоплению лишних SVG-узлов или canvas-контекстов, что ухудшает производительность при длительной работе интерфейса.