Контролы в OpenLayers представляют собой независимые UI-компоненты,
которые размещаются поверх карты и взаимодействуют с её состоянием.
Каждый контрол — это объект, связанный с экземпляром карты и содержащий
собственный DOM-элемент. Базовая модель строится вокруг класса
Control, который определяет жизненный цикл, привязку к
карте и механизм отрисовки.
Контролы работают вне слоя отображения карты: они не участвуют в рендеринге тайлов или векторных объектов, а управляют интерфейсом пользователя, состоянием карты или вызывают внешние действия.
Ключевые особенности системы контролов:
Все пользовательские контролы наследуются от
ol/control/Control. Этот класс принимает объект
конфигурации, содержащий DOM-элемент и функции жизненного цикла.
Минимальная структура кастомного контрола:
import Control from 'ol/control/Control.js';
class MyControl extends Control {
constructor(options = {}) {
const button = document.createElement('button');
button.innerHTML = 'OK';
const element = document.createElement('div');
element.className = 'my-control ol-unselectable ol-control';
element.appendChild(button);
super({
element: element,
target: options.target,
});
button.addEventListener('click', this.handleClick.bind(this));
}
handleClick() {
console.log('Контрол активирован');
}
}
Здесь ключевым моментом является передача element в
super(). OpenLayers использует его для вставки в DOM
карты.
Контрол не существует сам по себе — он всегда связан с экземпляром
карты. Добавление происходит через метод addControl.
import Map from 'ol/Map.js';
import View from 'ol/View.js';
const map = new Map({
target: 'map',
view: new View({
center: [0, 0],
zoom: 2,
}),
});
const control = new MyControl();
map.addControl(control);
После добавления OpenLayers автоматически размещает контрол в одном из контейнеров интерфейса.
Контролы размещаются в фиксированных зонах интерфейса карты. За это
отвечает свойство target и CSS-классы позиционирования.
Стандартные зоны:
ol-control-top-leftol-control-top-rightol-control-bottom-leftol-control-bottom-rightПример задания позиции:
import { defaults as defaultControls } from 'ol/control.js';
const map = new Map({
target: 'map',
controls: defaultControls().extend([new MyControl()]),
});
Внутри CSS можно управлять расположением:
.my-control {
position: absolute;
top: 10px;
left: 10px;
}
Контрол проходит несколько этапов:
Метод setMap можно переопределить для реакции на
подключение:
setMap(map) {
super.setMap(map);
if (map) {
console.log('Контрол добавлен на карту');
}
}
Контрол может взаимодействовать с картой через объект
map. Это позволяет изменять центр, масштаб, слои и другие
параметры.
Пример управления масштабом:
handleClick() {
const map = this.getMap();
if (!map) return;
const view = map.getView();
const zoom = view.getZoom();
view.setZoom(zoom + 1);
}
Такой подход делает контрол фактически инструментом управления состоянием карты.
Контролы могут содержать сложные интерфейсы: формы, переключатели, списки.
Пример контрола с полем ввода:
class SearchControl extends Control {
constructor() {
const input = document.createElement('input');
input.type = 'text';
input.placeholder = 'Поиск';
const button = document.createElement('button');
button.textContent = 'Найти';
const container = document.createElement('div');
container.className = 'search-control ol-control';
container.appendChild(input);
container.appendChild(button);
super({ element: container });
button.addEventListener('click', () => {
this.search(input.value);
});
}
search(query) {
const map = this.getMap();
if (!map) return;
console.log('Поиск:', query);
}
}
Контролы часто используют события DOM и события карты одновременно. Важно предотвращать всплытие событий, чтобы взаимодействие с интерфейсом не влияло на карту.
element.addEventListener('click', (e) => {
e.stopPropagation();
});
Для интеграции с картой используются события OpenLayers:
map.on('moveend', () => {
console.log('Карта перемещена');
});
Контролы требуют явного CSS для корректного отображения. OpenLayers предоставляет базовые классы, но внешний вид задаётся разработчиком.
Пример базового оформления:
.my-control {
background: white;
border-radius: 4px;
padding: 6px;
box-shadow: 0 2px 6px rgba(0,0,0,0.3);
}
.my-control button {
background: transparent;
border: none;
cursor: pointer;
}
Класс ol-unselectable предотвращает выделение текста
внутри контрола, что важно для UX.
Контрол может хранить внутреннее состояние, например активен/неактивен.
class ToggleControl extends Control {
constructor() {
const button = document.createElement('button');
button.textContent = 'OFF';
const element = document.createElement('div');
element.className = 'toggle-control ol-control';
element.appendChild(button);
super({ element });
this.active = false;
button.addEventListener('click', () => {
this.active = !this.active;
button.textContent = this.active ? 'ON' : 'OFF';
});
}
}
Такой подход позволяет строить переключатели слоёв, режимов взаимодействия и фильтров.
Контролы часто управляют слоями: включают, выключают, изменяют прозрачность.
toggleLayer() {
const map = this.getMap();
const layer = map.getLayers().item(0);
layer.setVisible(!layer.getVisible());
}
Расширенные контролы могут работать с коллекцией слоёв и динамически строить интерфейс.
Реализация собственного zoom-контрола демонстрирует базовые принципы управления картой.
class ZoomControl extends Control {
constructor() {
const zoomIn = document.createElement('button');
zoomIn.textContent = '+';
const zoomOut = document.createElement('button');
zoomOut.textContent = '-';
const container = document.createElement('div');
container.className = 'zoom-control ol-control';
container.appendChild(zoomIn);
container.appendChild(zoomOut);
super({ element: container });
zoomIn.addEventListener('click', () => this.changeZoom(1));
zoomOut.addEventListener('click', () => this.changeZoom(-1));
}
changeZoom(delta) {
const map = this.getMap();
if (!map) return;
const view = map.getView();
view.setZoom(view.getZoom() + delta);
}
}
При сложных интерфейсах контрол должен реагировать на изменения карты извне. Например, обновлять отображение текущего масштаба.
class ZoomDisplayControl extends Control {
constructor() {
const element = document.createElement('div');
element.className = 'zoom-display ol-control';
super({ element });
this.element = element;
}
setMap(map) {
super.setMap(map);
if (map) {
map.getView().on('change:resolution', () => {
const zoom = map.getView().getZoom();
this.element.textContent = `Zoom: ${zoom}`;
});
}
}
}
Контрол может генерировать собственные события для взаимодействия с внешним кодом.
class EventControl extends Control {
constructor() {
const button = document.createElement('button');
button.textContent = 'Emit';
const element = document.createElement('div');
element.className = 'event-control ol-control';
element.appendChild(button);
super({ element });
button.addEventListener('click', () => {
this.dispatchEvent('custom:event');
});
}
}
Подписка на событие:
const control = new EventControl();
control.on('custom:event', () => {
console.log('Событие контрола');
});
При разработке сложных контролов важно учитывать несколько принципов:
Контролы становятся устойчивыми компонентами интерфейса только при строгом разделении логики и представления.
OpenLayers предоставляет готовые контролы, такие как масштабирование, атрибуция, поворот. Их можно расширять, переопределяя поведение или внешний вид.
import Zoom from 'ol/control/Zoom.js';
class CustomZoom extends Zoom {
constructor(options) {
super(options);
this.element.classList.add('custom-zoom');
}
}
Такой подход позволяет переиспользовать базовую логику и изменять только визуальную или частично функциональную часть.