В Leaflet viewport карты представляет собой область отображения карты
внутри DOM-контейнера. Это не абстрактное понятие уровня браузера, а
конкретная геометрия элемента .leaflet-container, которая
определяет:
Любое изменение размеров контейнера напрямую влияет на внутренние расчёты Leaflet. В отличие от статичных изображений, карта не перерисовывается полностью при каждом изменении, а адаптирует существующую тайловую сетку.
Leaflet рассчитывает viewport в момент инициализации карты. Если контейнер изменяет размеры после создания экземпляра карты, возникает рассинхронизация:
Наиболее частые причины:
Leaflet не использует автоматический наблюдатель за изменениями DOM. Обновление viewport выполняется вручную через метод пересчёта размеров:
map.invalidateSize();
Этот вызов:
Дополнительные параметры:
map.invalidateSize({
animate: true,
pan: false
});
animate включает плавное смещение карты к новому
центру;pan определяет, нужно ли корректировать позицию.Основной сценарий адаптации viewport — обработка события изменения размеров окна:
window.addEventListener('resize', () => {
map.invalidateSize();
});
В этом режиме карта синхронизируется с новым размером контейнера, который может зависеть от CSS flex/grid макета.
Однако прямое использование события resize имеет особенности:
Более точный механизм адаптации основан на
ResizeObserver, который отслеживает именно контейнер карты,
а не окно браузера:
const container = document.getElementById('map');
const observer = new ResizeObserver(() => {
map.invalidateSize();
});
observer.observe(container);
Преимущества подхода:
Частая проблема возникает, когда карта создаётся в скрытом элементе:
display: none);В таких случаях Leaflet не может корректно вычислить размеры, так как
offsetWidth и offsetHeight равны нулю.
Корректный сценарий инициализации:
function showMap() {
mapContainer.style.display = 'block';
map.invalidateSize();
}
Ключевой момент — вызов пересчёта должен происходить после того, как браузер уже применил стили и отрисовал элемент.
Современные интерфейсы часто используют flexbox или CSS grid. В этих случаях контейнер карты может изменять размеры без явного события resize окна.
Особенности:
Типичный паттерн:
.layout {
display: flex;
height: 100vh;
}
.sidebar {
width: 300px;
}
.map {
flex: 1;
}
При сворачивании sidebar карта изменяет размер, и требуется явный вызов пересчёта.
При переходе в полноэкранный режим изменяется viewport браузера, но не всегда корректно обновляется контейнер карты.
document.addEventListener('fullscreenchange', () => {
map.invalidateSize();
});
Особенности поведения:
На мобильных устройствах viewport усложняется из-за:
Обработка ориентации:
window.addEventListener('orientationchange', () => {
setTimeout(() => {
map.invalidateSize();
}, 200);
});
Задержка позволяет системе завершить перерасчёт layout до обновления карты.
На устройствах с вырезами и нестандартными границами (notch) viewport может не совпадать с визуальной областью.
CSS-адаптация:
.map {
padding-top: env(safe-area-inset-top);
padding-bottom: env(safe-area-inset-bottom);
}
Leaflet учитывает только размеры контейнера, поэтому любые safe-area
корректировки должны быть отражены в DOM перед вызовом
invalidateSize.
При интенсивных изменениях размера (drag панели, анимации интерфейса) частые вызовы пересчёта могут влиять на производительность.
Типичный способ оптимизации:
let resizeTimeout;
window.addEventListener('resize', () => {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
map.invalidateSize();
}, 100);
});
Такой подход снижает нагрузку за счёт батчинга операций.
При изменении viewport важно сохранять:
Leaflet по умолчанию сохраняет состояние, но при сложных сценариях (например, ручной reset view) может потребоваться явное управление:
const center = map.getCenter();
const zoom = map.getZoom();
map.invalidateSize();
map.setView(center, zoom);
Изменение размеров влияет на:
После invalidateSize Leaflet пересчитывает tile grid,
что может приводить к кратковременной загрузке новых сегментов карты,
особенно при резком увеличении контейнера.