AutoSizer и адаптивность

В основе адаптивного поведения Kepler.gl лежит механизм динамического измерения контейнера, реализованный через компонент AutoSizer из экосистемы react-virtualized. Его задача заключается в постоянном отслеживании размеров родительского DOM-элемента и передаче актуальных значений ширины и высоты в компонент карты. Это позволяет визуализации занимать всё доступное пространство без ручного управления размерами.

Kepler.gl строит интерфейс вокруг идеи «карта как потоковый рендеринг внутри контейнера», где размеры не задаются жестко, а вычисляются в момент рендера и при каждом изменении layout.


Принцип работы AutoSizer

AutoSizer использует наблюдение за изменениями размеров контейнера и обновляет параметры через React state/props механизм.

Основные этапы работы:

  • измерение DOM-элемента через getBoundingClientRect
  • подписка на изменения размеров окна
  • обработка resize-событий
  • прокидывание актуальных значений width и height в дочерний компонент

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


Роль AutoSizer в архитектуре Kepler.gl

В Kepler.gl AutoSizer используется как обертка над основным компонентом карты. Он обеспечивает:

  • автоматическое масштабирование WebGL-контекста
  • синхронизацию слоя визуализации с размерами контейнера
  • корректное отображение панели слоев и инструментов
  • адаптацию при изменении layout приложения

В типовой структуре Kepler.gl компонент выглядит следующим образом:

import KeplerGl from 'kepler.gl';
import AutoSizer from 'react-virtualized/dist/commonjs/AutoSizer';

const MapWrapper = () => (
  <div style={{ height: '100vh', width: '100%' }}>
    <AutoSizer>
      {({ height, width }) => (
        <KeplerGl
          id="map"
          width={width}
          height={height}
        />
      )}
    </AutoSizer>
  </div>
);

Здесь AutoSizer выступает источником «истины» по размерам контейнера.


Контейнерная модель и влияние CSS

Корректная работа AutoSizer зависит от цепочки родительских элементов. Если хотя бы один контейнер не имеет вычисляемой высоты, AutoSizer возвращает нулевые значения.

Типичная зависимость:

  • html, body должны иметь высоту 100%
  • корневой div должен иметь явную высоту
  • flex-контейнеры должны корректно распределять пространство

Пример корректной структуры:

html, body {
  height: 100%;
  margin: 0;
}

#root {
  height: 100%;
  display: flex;
}

Kepler.gl в таком окружении получает стабильные размеры и не «схлопывается» при инициализации.


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

AutoSizer отслеживает изменения окна и контейнера, что особенно важно для сценариев:

  • открытие/закрытие боковых панелей
  • изменение layout в dashboard
  • responsive-переключения (mobile/desktop)
  • изменение размеров split-view интерфейсов

При каждом изменении инициируется пересчет, после чего Kepler.gl обновляет WebGL viewport:

  • пересоздание projection матрицы
  • перерасчет тайлов
  • обновление слоя визуализации

Интеграция с flexbox и grid-layout

AutoSizer наиболее стабильно работает в flex- и grid-контейнерах при соблюдении одного условия: родитель должен иметь ограниченную высоту.

Пример flex-раскладки:

.wrapper {
  display: flex;
  height: 100vh;
}

.sidebar {
  width: 300px;
}

.mapContainer {
  flex: 1;
  position: relative;
}
<div className="wrapper">
  <div className="sidebar" />
  <div className="mapContainer">
    <AutoSizer>
      {({ width, height }) => (
        <KeplerGl id="map" width={width} height={height} />
      )}
    </AutoSizer>
  </div>
</div>

Такая структура обеспечивает предсказуемую геометрию без скачков интерфейса.


WebGL и пересчет viewport

Kepler.gl использует WebGL для рендеринга, поэтому изменение размеров влияет не только на DOM, но и на графический контекст.

При обновлении размеров выполняются операции:

  • gl.viewport(0, 0, width, height)
  • перерасчет камеры (latitude/longitude bounds)
  • обновление пиксельной плотности (devicePixelRatio)

AutoSizer выступает триггером для синхронизации этих параметров.


Поведение при динамическом изменении UI

Особое значение AutoSizer приобретает при наличии анимаций и динамических панелей:

  • раскрытие фильтров данных
  • открытие легенды
  • изменение ширины sidebar
  • переходы между режимами анализа

Каждое изменение контейнера приводит к повторному измерению, что может вызывать частые ререндеры.

Для стабилизации поведения применяются:

  • debounce resize-обработки
  • мемоизация контейнеров
  • изоляция карты в отдельном layout-слое

Производительность и оптимизация

Частые изменения размеров могут приводить к нагрузке на WebGL-контекст. Основные источники затрат:

  • перерасчет тайлов карты
  • перерисовка большого количества слоев
  • обновление projection matrix

Оптимизационные подходы:

  • минимизация количества родительских reflow
  • использование will-change: transform для контейнеров
  • предотвращение layout thrashing
  • ограничение частоты resize событий

AutoSizer сам по себе легковесен, но становится критичным узлом в больших дашбордах.


Ограничения и особенности поведения

Несмотря на универсальность, механизм имеет ряд особенностей:

  • невозможность корректного измерения при display: none
  • некорректная работа при отсутствии явной высоты у родителей
  • возможные задержки при сложных DOM-деревьях
  • зависимость от корректного CSS-лейаута

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


Поведение в SSR и гидратации

В server-side rendering AutoSizer не может вычислить реальные размеры DOM. В результате:

  • initial width/height могут быть нулевыми
  • карта инициализируется после гидратации
  • происходит вторичный рендер после mount

Типичная схема:

  • SSR: заглушка контейнера
  • client mount: измерение DOM
  • обновление Kepler.gl viewport

Это поведение является нормальным для всех DOM-based измерений.


Взаимодействие с React lifecycle

AutoSizer интегрируется в жизненный цикл React через:

  • componentDidMount — первичное измерение
  • componentDidUpdate — реакция на изменения layout
  • window.resize listener — глобальные изменения viewport

В функциональном стиле используется аналог через hooks внутри Kepler.gl, где AutoSizer передает значения через render props.


Типичные архитектурные паттерны

В крупных приложениях AutoSizer часто используется в сочетании с:

  • Redux-состоянием layout
  • динамическими панелями фильтрации
  • multi-map режимами
  • вкладками с ленивой инициализацией

Распространённый паттерн — изоляция карты в отдельный контейнер, полностью управляемый AutoSizer, чтобы исключить влияние внешнего layout.


Поведение при масштабировании интерфейса

При изменении масштабирования браузера или DPI AutoSizer не всегда пересчитывает размеры напрямую, однако Kepler.gl реагирует через:

  • изменение devicePixelRatio
  • обновление canvas resolution
  • пересчет WebGL buffers

Это обеспечивает сохранение четкости визуализации при масштабировании интерфейса.