Позиционирование контролов

Mapbox GL JS предоставляет систему контролов (controls), которая позволяет управлять элементами интерфейса карты — масштабированием, навигацией, геолокацией и пользовательскими компонентами. Одним из ключевых аспектов работы с контролами является их позиционирование внутри контейнера карты и управление порядком отображения.

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

Метод добавления контролов:

map.addControl(control, position);

Параметр position принимает одно из фиксированных значений:

  • top-left
  • top-right
  • bottom-left
  • bottom-right

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

Внутри Mapbox GL JS каждая зона реализована как отдельный блок с классом вида:

  • mapboxgl-ctrl-top-left
  • mapboxgl-ctrl-top-right
  • mapboxgl-ctrl-bottom-left
  • mapboxgl-ctrl-bottom-right

Эти контейнеры располагаются поверх карты и используют flex-раскладку по вертикали.

Порядок отображения внутри одной позиции

Контролы внутри одной зоны отображаются в порядке их добавления:

map.addControl(new mapboxgl.NavigationControl(), 'top-right');
map.addControl(new mapboxgl.FullscreenControl(), 'top-right');

В этом случае NavigationControl окажется выше в DOM-структуре (и визуально выше/левее в зависимости от flex-направления), а FullscreenControl будет добавлен ниже.

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

Встроенные контролы и их позиционирование

Часто используемые контролы:

map.addControl(new mapboxgl.NavigationControl(), 'top-right');
map.addControl(new mapboxgl.GeolocateControl(), 'top-right');
map.addControl(new mapboxgl.FullscreenControl(), 'top-right');
map.addControl(new mapboxgl.ScaleControl(), 'bottom-left');

Особенности поведения:

  • NavigationControl обычно группирует zoom и rotation в одном блоке.
  • GeolocateControl активирует доступ к геопозиции пользователя.
  • ScaleControl фиксируется в нижней части карты и редко изменяет положение.

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

CSS-структура контейнеров контролов

Контейнеры контролов имеют базовую структуру:

<div class="mapboxgl-ctrl-top-right">
  <div class="mapboxgl-ctrl-group">
    <!-- controls -->
  </div>
</div>

Ключевые классы:

  • mapboxgl-ctrl — базовый класс любого контрола
  • mapboxgl-ctrl-group — группа кнопок (например, zoom + rotate)
  • mapboxgl-ctrl-top-* — позиционные контейнеры

Стили по умолчанию используют абсолютное позиционирование относительно карты и z-index для перекрытия тайлов и слоёв.

Перекрытие и приоритет слоёв интерфейса

Контролы располагаются поверх canvas-контейнера карты. Их порядок определяется:

  1. Позиционной зоной (top-left, top-right и т.д.)
  2. Порядком добавления
  3. CSS-свойствами z-index

Если требуется, чтобы кастомный интерфейс перекрывал контролы, необходимо учитывать, что стандартные контролы имеют достаточно высокий z-index, поэтому простое добавление HTML-элемента может быть недостаточным.

Кастомные контролы и IControl

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

const customControl = {
  onAdd(map) {
    this._map = map;
    this._container = document.createElement('div');
    this._container.className = 'mapboxgl-ctrl mapboxgl-ctrl-group';
    this._container.textContent = 'Custom';
    return this._container;
  },

  onRemove() {
    this._container.parentNode.removeChild(this._container);
    this._map = undefined;
  }
};

map.addControl(customControl, 'bottom-right');

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

Управление динамическим расположением

Контролы можно удалять и перемещать:

map.removeControl(control);
map.addControl(control, 'bottom-left');

Это позволяет динамически менять интерфейс в зависимости от состояния приложения, например:

  • переключение мобильной/десктопной версии
  • изменение компоновки при открытии сайдбара
  • адаптация под разные размеры экрана

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

Система имеет фиксированную модель из четырёх зон. Нельзя напрямую задать произвольные координаты (например, центр карты или произвольный пиксельный offset) через addControl.

Попытки изменить позиционирование через внутренние DOM-структуры (например, через приватные контейнеры карты) приводят к нестабильности при обновлениях библиотеки.

Адаптивное позиционирование

Для адаптивных интерфейсов используется комбинация JavaScript и CSS:

function updateControlsPosition() {
  const isMobile = window.innerWidth < 600;

  map.removeControl(nav);
  map.addControl(nav, isMobile ? 'bottom-right' : 'top-right');
}

Такой подход позволяет перераспределять интерфейсные элементы без пересоздания карты.

Дополнительно часто применяются медиазапросы для изменения внешнего вида контейнеров:

.mapboxgl-ctrl-top-right {
  margin-top: 10px;
  margin-right: 10px;
}

Вложенность и группировка контролов

Несколько контролов могут быть визуально объединены в одну группу через mapboxgl-ctrl-group. Это влияет только на внешний вид, но не на позиционирование:

  • группа остаётся в пределах одной из четырёх зон
  • элементы внутри группы располагаются вертикально

Пример:

map.addControl(new mapboxgl.NavigationControl(), 'top-left');
map.addControl(new mapboxgl.FullscreenControl(), 'top-left');

При этом каждый контрол будет в своей группе, если не объединён вручную.

Особенности поведения при масштабировании карты

При изменении масштаба карты или её контейнера контролы:

  • не пересчитывают позицию относительно карты
  • остаются закреплёнными в DOM-контейнере
  • реагируют только на изменение размеров viewport

Это важно при работе с responsive layout, особенно при изменении высоты/ширины контейнера карты динамически.

Влияние порядка загрузки карты

Контролы добавляются только после инициализации объекта карты. При попытке добавить контрол до события load возможны некорректные состояния DOM.

Типичный безопасный паттерн:

map.on('load', () => {
  map.addControl(new mapboxgl.NavigationControl(), 'top-right');
});

Позиционирование при этом полностью определяется моментом добавления, а не порядком объявления.

Переключение позиций без удаления карты

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

  • removeControl
  • повторный addControl
  • или манипуляции CSS display: none на контейнерах .mapboxgl-ctrl-*

Второй вариант предпочтительнее, так как сохраняет внутреннее состояние контрола.

Практические особенности DOM-структуры

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

  • map container

    • canvas
    • control containers (4 зоны)
    • overlays

Контролы всегда находятся выше canvas слоя, но ниже пользовательских overlay-элементов, если те добавлены отдельно в DOM.


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