Класс Control

Класс L.Control представляет базовый механизм расширения интерфейса карты. Он используется для создания элементов управления, которые размещаются поверх карты и взаимодействуют с пользователем независимо от слоёв данных. В архитектуре Leaflet контролы отделены от географических объектов и служат исключительно для UI-логики: кнопок, панелей, переключателей слоёв, шкал масштаба и пользовательских виджетов.


Базовая концепция L.Control

L.Control — абстрактный класс. Он не используется напрямую, а служит основой для наследования. Любой контроль в Leaflet реализует жизненный цикл:

  • создание DOM-элемента (onAdd)
  • удаление DOM-элемента (onRemove)
  • добавление на карту (addTo)
  • управление позиционированием

Контрол всегда привязан к экземпляру карты и существует в пределах её контейнера.


Основные опции Control

Каждый контроль принимает набор стандартных параметров:

  • position — позиция на карте Возможные значения:

    • topleft
    • topright
    • bottomleft
    • bottomright
  • prefix — CSS-префикс (чаще используется внутри встроенных контролов)

Пример базовой конфигурации:

L.control({ position: 'topright' });

Жизненный цикл контроля

Метод onAdd(map)

onAdd вызывается при добавлении контроля на карту. Он должен вернуть DOM-элемент, который будет вставлен в интерфейс.

L.Control.MyControl = L.Control.extend({
    onAdd: function(map) {
        const container = L.DomUtil.create('div', 'my-control');
        container.innerHTML = 'Контроль';
        return container;
    }
});

Ключевые особенности:

  • создаётся DOM-узел
  • можно использовать L.DomUtil
  • обязательно возвращается HTML-элемент
  • здесь подключается вся визуальная логика

Метод onRemove(map)

Вызывается при удалении контроля с карты. Используется для очистки событий и освобождения ресурсов.

onRemove: function(map) {
    // очистка обработчиков
}

Важно учитывать, что Leaflet не всегда автоматически уничтожает вложенные обработчики, если они были добавлены вручную.


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

Любой контроль добавляется через метод addTo:

const control = L.control({ position: 'topright' });
control.addTo(map);

или через сокращённую запись:

L.control({ position: 'topright' }).addTo(map);

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

Для расширения L.Control используется L.Control.extend.

Базовый шаблон

L.Control.SimpleButton = L.Control.extend({
    options: {
        position: 'topright'
    },

    onAdd: function(map) {
        const container = L.DomUtil.create('div', 'leaflet-bar leaflet-control');
        const button = L.DomUtil.create('a', '', container);

        button.innerHTML = '⤢';
        button.href = '#';

        L.DomEvent.on(button, 'click', function(e) {
            L.DomEvent.preventDefault(e);
            console.log('Кнопка нажата');
        });

        return container;
    }
});

Регистрация контроля

После определения класса создаётся фабричная функция:

L.control.simpleButton = function(opts) {
    return new L.Control.SimpleButton(opts);
};

Использование:

L.control.simpleButton({ position: 'bottomleft' }).addTo(map);

Работа с DOM внутри Control

Leaflet предоставляет утилиты:

  • L.DomUtil.create(tagName, className, container?)
  • L.DomEvent.on(element, event, handler)
  • L.DomEvent.disableClickPropagation(element)
  • L.DomEvent.disableScrollPropagation(element)

Пример защиты от прокрутки карты

onAdd: function(map) {
    const container = L.DomUtil.create('div', 'control-box');
    L.DomEvent.disableScrollPropagation(container);
    L.DomEvent.disableClickPropagation(container);
    return container;
}

Это предотвращает случайное перемещение карты при взаимодействии с контролом.


Встроенные контролы Leaflet

Масштаб (Zoom Control)

L.control.zoom({
    position: 'topright'
}).addTo(map);

Функции:

  • увеличение/уменьшение масштаба
  • кнопки “+” и “−”
  • управление уровнем zoom

Контроль слоёв (Layers Control)

Позволяет переключать базовые и оверлейные слои.

const baseMaps = {
    "OSM": osmLayer,
    "Satellite": satelliteLayer
};

const overlays = {
    "Markers": markersLayer
};

L.control.layers(baseMaps, overlays).addTo(map);

Особенности:

  • управление видимостью слоёв
  • группировка
  • автоматическое обновление UI при изменении карты

Атрибуция (Attribution Control)

Отображает текст авторства картографических данных.

map.attributionControl.addAttribution('Custom data © company');

или отключение:

L.control.attribution({ position: 'bottomright' }).addTo(map);

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

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

  • .leaflet-top
  • .leaflet-bottom
  • .leaflet-left
  • .leaflet-right

Внутри этих областей контролы выстраиваются вертикально.


Стилизация контролов

Контролы используют стандартные CSS-классы Leaflet:

  • leaflet-control
  • leaflet-bar
  • leaflet-control-zoom

Пример кастомного стиля:

.my-control {
    background: white;
    padding: 8px;
    border-radius: 4px;
    box-shadow: 0 1px 5px rgba(0,0,0,0.3);
}

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


Обработка событий внутри Control

Контролы часто содержат интерактивные элементы. Основные события:

  • click
  • mouseover
  • mouseout
  • keydown

Пример:

L.DomEvent.on(button, 'click', function(e) {
    L.DomEvent.stopPropagation(e);
    alert('Действие выполнено');
});

Управление состоянием контроля

Контрол может хранить состояние:

L.Control.Toggle = L.Control.extend({
    onAdd: function(map) {
        this._active = false;

        const container = L.DomUtil.create('div', 'toggle-control');
        const button = L.DomUtil.create('button', '', container);

        button.innerHTML = 'OFF';

        button.oncl ick = () => {
            this._active = !this._active;
            button.innerHTML = this._active ? 'ON' : 'OFF';
        };

        return container;
    }
});

Состояние обычно хранится в свойствах объекта контроля.


Взаимодействие Control и Map

Контрол получает доступ к карте через аргумент onAdd(map) и может:

  • изменять слои
  • слушать события карты (move, zoom, click)
  • управлять состоянием отображения

Пример подписки:

onAdd: function(map) {
    this._map = map;

    map.on('zoomend', this._update, this);
    return L.DomUtil.create('div', 'info');
}

Удаление и очистка ресурсов

Корректное удаление важно для предотвращения утечек:

onRemove: function(map) {
    map.off('zoomend', this._update, this);
}

Если контроль содержит таймеры или сторонние обработчики, они также должны быть очищены здесь.


Расширенные паттерны использования

Контроль как мини-приложение

Контроль может включать:

  • формы ввода
  • фильтры данных
  • переключатели слоёв
  • динамическое обновление маркеров

Асинхронная загрузка данных

L.Control.DataLoader = L.Control.extend({
    onAdd: function(map) {
        const container = L.DomUtil.create('div');

        fetch('/api/data')
            .then(r => r.json())
            .then(data => {
                container.innerHTML = data.length + ' объектов';
            });

        return container;
    }
});

Контроль с реакцией на карту

map.on('moveend', function() {
    console.log('Карта перемещена');
});

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


Архитектурная роль L.Control

L.Control выполняет роль слоя интерфейса поверх геопространственной модели. Он:

  • отделяет UI от геометрии
  • стандартизирует размещение элементов
  • обеспечивает единый жизненный цикл
  • интегрируется с системой событий карты

Контролы формируют каркас пользовательского взаимодействия, не вмешиваясь в геоданные и рендеринг слоёв.