Отзывчивость карты в Google Maps Platform строится не на самой карте
как объекте, а на HTML-контейнере, в который она встроена. Объект карты
(google.maps.Map) не управляет размерами самостоятельно —
он лишь подстраивается под размеры DOM-элемента.
Ключевой момент: если контейнер изменяет размеры, карта не всегда автоматически перерасчитывает тайлы и центр без явного уведомления API.
Минимальная структура:
<div id="map"></div>
#map {
width: 100%;
height: 400px;
}
const map = new google.maps.Map(document.getElementById("map"), {
center: { lat: 55.751244, lng: 37.618423 },
zoom: 10,
});
Отзывчивость на этом уровне достигается исключительно через CSS
(width: 100%, vh, flex/grid).
Современные интерфейсы редко используют фиксированную высоту. Чаще применяются:
vhПример flex-раскладки:
.layout {
display: flex;
height: 100vh;
}
.sidebar {
width: 320px;
}
.map {
flex: 1;
}
<div class="layout">
<div class="sidebar"></div>
<div id="map" class="map"></div>
</div>
В этом случае карта может менять размер при каждом изменении ширины окна без перезагрузки.
Одна из ключевых особенностей Google Maps JavaScript API: если карта
инициализирована в скрытом контейнере (display: none), её
рендер может оказаться некорректным.
Типичные сценарии:
При первом отображении карта может быть «обрезанной» или серой плиткой.
Решение — принудительное уведомление API:
google.maps.event.trigger(map, "resize");
После этого обычно требуется восстановление центра:
map.setCenter({ lat: 55.751244, lng: 37.618423 });
Более устойчивый подход — отслеживание изменений контейнера через
ResizeObserver.
const mapContainer = document.getElementById("map");
const observer = new ResizeObserver(() => {
google.maps.event.trigger(map, "resize");
});
observer.observe(mapContainer);
Преимущество этого подхода:
Классический способ:
window.addEventListener("resize", () => {
google.maps.event.trigger(map, "resize");
});
Недостаток — реагирует только на viewport, игнорируя внутренние изменения layout.
Поэтому используется как дополнительный, а не основной механизм.
При перерасчёте размеров карта может смещаться визуально. Для стабильного поведения используется фиксация состояния:
let center = map.getCenter();
let zoom = map.getZoom();
google.maps.event.trigger(map, "resize");
map.setCenter(center);
map.setZoom(zoom);
В сложных интерфейсах это предотвращает «прыжки» карты при изменении контейнера.
Мобильная отзывчивость карты зависит от двух факторов:
vh)Типичный паттерн:
#map {
height: 100vh;
}
Однако на мобильных браузерах 100vh может включать
скрытые панели интерфейса. Более точный вариант:
#map {
height: calc(var(--vh, 1vh) * 100);
}
Инициализация переменной:
function setVh() {
document.documentElement.style.setProperty(
"--vh",
window.innerHeight * 0.01 + "px"
);
}
window.addEventListener("resize", setVh);
setVh();
При смене ориентации (portrait ↔︎ landscape):
Типовая обработка:
window.addEventListener("orientationchange", () => {
google.maps.event.trigger(map, "resize");
});
В современных браузерах resize часто достаточно, но
ориентация добавляет стабильности.
При слишком малой высоте контейнера карта теряет функциональность интерфейса:
Практический минимум:
#map {
min-height: 200px;
}
В интерфейсах с плотной компоновкой:
#map {
min-height: 300px;
}
Grid даёт наиболее предсказуемую отзывчивость:
.wrapper {
display: grid;
grid-template-columns: 1fr 2fr;
height: 100vh;
}
#map {
width: 100%;
height: 100%;
}
Изменение сетки автоматически меняет геометрию карты без
дополнительных вызовов, но при резких перестройках всё равно требуется
resize.
Иногда карта встроена в интерфейс, где размеры зависят от состояния приложения:
function toggleSidebar(open) {
document.body.classList.toggle("sidebar-open", open);
google.maps.event.trigger(map, "resize");
}
Такой подход характерен для SPA, где DOM перестраивается без перезагрузки страницы.
Частые вызовы resize могут приводить к:
Оптимизация через debounce:
function debounce(fn, delay) {
let t;
return (...args) => {
clearTimeout(t);
t = setTimeout(() => fn(...args), delay);
};
}
const onRes ize = debounce(() => {
google.maps.event.trigger(map, "resize");
}, 150);
window.addEventListener("resize", onResize);
Частый кейс: карта находится во вкладке, которая активируется позже.
Корректный порядок:
resizefunction onTabActivated() {
requestAnimationFrame(() => {
google.maps.event.trigger(map, "resize");
map.setCenter(map.getCenter());
});
}
requestAnimationFrame гарантирует, что DOM уже
пересчитан.
Современный CSS позволяет задавать пропорции:
#map {
width: 100%;
aspect-ratio: 16 / 9;
}
Такой подход полезен для:
При изменении ширины высота пересчитывается автоматически, сохраняя пропорции, а карта адаптируется без дополнительной логики, если контейнер уже видим.
Если карта создаётся после загрузки данных, возможна ситуация, когда контейнер ещё не получил финальные размеры.
Решение — отложенная инициализация:
setTimeout(() => {
const map = new google.maps.Map(document.getElementById("map"), {
center: { lat: 55.751244, lng: 37.618423 },
zoom: 10,
});
}, 0);
Либо через requestAnimationFrame:
requestAnimationFrame(() => {
new google.maps.Map(document.getElementById("map"), options);
});
При быстром переключении размеров (например, drag панели) возможны артефакты тайлов.
Практика стабилизации:
requestAnimationFrameОтзывчивость не должна разрушать состояние:
resize не влияет на данные слоя, но влияет на viewport.
Поэтому при проектировании важно разделять:
Это предотвращает деградацию UX при частых изменениях интерфейса.