Пользовательские контролы

Пользовательские контролы (Custom Controls) в Google Maps JavaScript API позволяют добавлять на карту собственные элементы управления, полностью адаптированные под требования приложения. В отличие от стандартных элементов интерфейса, таких как кнопки масштабирования, переключатель типа карты или полноэкранный режим, пользовательские контролы могут выполнять любые действия:

  • изменение центра карты;
  • переключение режимов отображения;
  • фильтрация маркеров;
  • запуск поиска;
  • отображение дополнительной информации;
  • управление слоями;
  • взаимодействие с внешними сервисами.

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


Принцип работы контролов

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

Добавление элемента выполняется через массив соответствующей позиции:

map.controls[google.maps.ControlPosition.TOP_CENTER].push(controlDiv);

Где:

  • map.controls — коллекция всех контролов;
  • ControlPosition — место размещения;
  • controlDiv — HTML-элемент.

Схематически процесс выглядит следующим образом:

  1. Создаётся HTML-элемент.
  2. Настраивается внешний вид.
  3. Добавляются обработчики событий.
  4. Элемент помещается в нужную область карты.
  5. Контрол становится частью интерфейса.

Доступные позиции контролов

Google Maps предоставляет множество предопределённых областей размещения.

google.maps.ControlPosition.TOP_LEFT
google.maps.ControlPosition.TOP_CENTER
google.maps.ControlPosition.TOP_RIGHT

google.maps.ControlPosition.LEFT_TOP
google.maps.ControlPosition.LEFT_CENTER
google.maps.ControlPosition.LEFT_BOTTOM

google.maps.ControlPosition.RIGHT_TOP
google.maps.ControlPosition.RIGHT_CENTER
google.maps.ControlPosition.RIGHT_BOTTOM

google.maps.ControlPosition.BOTTOM_LEFT
google.maps.ControlPosition.BOTTOM_CENTER
google.maps.ControlPosition.BOTTOM_RIGHT

Наиболее часто используются:

Позиция Описание
TOP_LEFT Верхний левый угол
TOP_RIGHT Верхний правый угол
TOP_CENTER Верхняя центральная область
BOTTOM_LEFT Нижний левый угол
BOTTOM_RIGHT Нижний правый угол

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


Создание простого контрола

Простейший контрол представляет собой кнопку.

function createControl(map) {

    const button = document.createElement("button");

    button.textContent = "Перейти в Москву";

    button.style.background = "#fff";
    button.style.border = "2px solid #fff";
    button.style.borderRadius = "3px";
    button.style.padding = "8px 16px";
    button.style.cursor = "pointer";

    button.addEventListener("click", () => {

        map.setCenter({
            lat: 55.7558,
            lng: 37.6176
        });

        map.setZoom(12);

    });

    map.controls[
        google.maps.ControlPosition.TOP_CENTER
    ].push(button);
}

После нажатия карта перемещается в Москву и устанавливает новый уровень масштабирования.


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

Инициализация карты

const map = new google.maps.Map(
    document.getElementById("map"),
    {
        center: {
            lat: 51.1694,
            lng: 71.4491
        },
        zoom: 6
    }
);

Создание контейнера

const controlDiv = document.createElement("div");

Создание кнопки

const button = document.createElement("button");

button.textContent = "Домой";

Добавление стилей

button.style.backgroundColor = "#ffffff";
button.style.border = "none";
button.style.padding = "10px 15px";
button.style.margin = "10px";
button.style.borderRadius = "4px";
button.style.boxShadow =
    "0 2px 6px rgba(0,0,0,0.3)";
button.style.cursor = "pointer";

Добавление поведения

button.addEventListener("click", () => {

    map.panTo({
        lat: 51.1694,
        lng: 71.4491
    });

});

Добавление на карту

controlDiv.appendChild(button);

map.controls[
    google.maps.ControlPosition.TOP_LEFT
].push(controlDiv);

Использование классов для контролов

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

class HomeControl {

    constructor(map) {

        this.map = map;

        this.element =
            document.createElement("button");

        this.initialize();
    }

    initialize() {

        this.element.textContent = "Домой";

        this.element.addEventListener(
            "click",
            () => {

                this.map.setCenter({
                    lat: 51.1694,
                    lng: 71.4491
                });

            }
        );
    }

}

Подключение:

const control = new HomeControl(map);

map.controls[
    google.maps.ControlPosition.TOP_LEFT
].push(control.element);

Такой подход облегчает поддержку кода и повторное использование компонентов.


Контрол переключения типа карты

Контрол может изменять тип отображения карты.

const button = document.createElement("button");

button.textContent = "Спутник";

button.addEventListener("click", () => {

    map.setMapTypeId("satellite");

});

Переключение обратно:

button.addEventListener("dblclick", () => {

    map.setMapTypeId("roadmap");

});

Возможные типы:

roadmap
satellite
hybrid
terrain

Пример переключателя:

let satellite = false;

button.addEventListener("click", () => {

    satellite = !satellite;

    map.setMapTypeId(
        satellite
            ? "satellite"
            : "roadmap"
    );

});

Контрол изменения масштаба

Пользовательские кнопки могут заменять стандартный Zoom Control.

Увеличение масштаба

zoomInButton.addEventListener(
    "click",
    () => {

        map.setZoom(
            map.getZoom() + 1
        );

    }
);

Уменьшение масштаба

zoomOutButton.addEventListener(
    "click",
    () => {

        map.setZoom(
            map.getZoom() - 1
        );

    }
);

Отключение стандартного контрола:

const map = new google.maps.Map(
    document.getElementById("map"),
    {
        zoomControl: false
    }
);

Контрол поиска текущего местоположения

Очень распространённый вариант пользовательского управления.

button.addEventListener("click", () => {

    navigator.geolocation.getCurrentPosition(
        (position) => {

            const location = {

                lat: position.coords.latitude,
                lng: position.coords.longitude

            };

            map.setCenter(location);

            new google.maps.Marker({
                position: location,
                map
            });

        }
    );

});

После нажатия карта автоматически перемещается к текущим координатам пользователя.


Контрол для управления маркерами

Часто требуется скрывать или отображать группы маркеров.

const markers = [];

Скрытие:

button.addEventListener("click", () => {

    markers.forEach(marker => {

        marker.setMap(null);

    });

});

Отображение:

button.addEventListener("click", () => {

    markers.forEach(marker => {

        marker.setMap(map);

    });

});

Переключение:

let visible = true;

button.addEventListener("click", () => {

    visible = !visible;

    markers.forEach(marker => {

        marker.setMap(
            visible ? map : null
        );

    });

});

Создание панели инструментов

Контрол не обязан состоять из одной кнопки.

Внутри контейнера можно разместить полноценную панель.

const panel =
    document.createElement("div");

Добавление кнопок:

panel.appendChild(zoomInButton);
panel.appendChild(zoomOutButton);
panel.appendChild(homeButton);
panel.appendChild(searchButton);

Размещение:

map.controls[
    google.maps.ControlPosition.RIGHT_TOP
].push(panel);

Так создаются компактные панели управления картой.


Использование CSS-классов

Вместо инлайн-стилей рекомендуется использовать CSS.

.map-control {
    background: white;
    border-radius: 4px;
    padding: 10px;
    margin: 10px;
    cursor: pointer;
    box-shadow:
        0 2px 6px rgba(0,0,0,.3);
}

Создание элемента:

const button =
    document.createElement("button");

button.classList.add("map-control");

Преимущества:

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

Работа с событиями карты внутри контролов

Контрол может реагировать на изменение состояния карты.

Например, отображать текущий масштаб.

const info =
    document.createElement("div");

Обновление значения:

map.addListener("zoom_changed", () => {

    info.textContent =
        `Zoom: ${map.getZoom()}`;

});

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


Создание информационного контрола

Пример отображения координат центра карты.

const info =
    document.createElement("div");

Обновление данных:

function updateInfo() {

    const center =
        map.getCenter();

    info.innerHTML = `
        Lat:
        ${center.lat().toFixed(5)}
        <br>
        Lng:
        ${center.lng().toFixed(5)}
    `;
}

Подписка на события:

map.addListener(
    "center_changed",
    updateInfo
);

Первичная инициализация:

updateInfo();

Контрол будет автоматически обновляться при перемещении карты.


Удаление пользовательского контрола

Для удаления можно использовать DOM-операции.

button.remove();

Или удалить родительский контейнер:

controlDiv.remove();

Если требуется временно скрыть элемент:

controlDiv.style.display = "none";

Повторное отображение:

controlDiv.style.display = "block";

Производительность пользовательских контролов

При разработке интерфейсов с большим количеством элементов рекомендуется:

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

Неэффективный вариант:

map.addListener(
    "bounds_changed",
    () => {

        expensiveCalculation();

    }
);

Более корректный вариант:

const update =
    debounce(expensiveCalculation, 300);

map.addListener(
    "bounds_changed",
    update
);

Совмещение пользовательских и стандартных контролов

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

Настройка карты:

const map = new google.maps.Map(
    document.getElementById("map"),
    {
        zoomControl: true,
        mapTypeControl: true,
        streetViewControl: true,
        fullscreenControl: true
    }
);

Добавление собственного контрола:

map.controls[
    google.maps.ControlPosition.TOP_RIGHT
].push(customControl);

Такой подход позволяет сохранить привычный интерфейс Google Maps и дополнить его специализированными возможностями приложения.


Архитектура крупных проектов

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

Пример структуры:

controls/
│
├── HomeControl.js
├── SearchControl.js
├── FilterControl.js
├── LayerControl.js
└── ZoomControl.js

Каждый компонент отвечает только за одну задачу:

  • навигацию;
  • фильтрацию данных;
  • переключение слоёв;
  • изменение масштаба;
  • отображение информации.

Подобная организация упрощает тестирование, поддержку и расширение функциональности картографического приложения на базе Google Maps JavaScript API.