В веб-картах на базе MapLibre GL JS курсор выступает ключевым индикатором состояния взаимодействия. Он отражает доступные действия: наведение на объект, возможность перетаскивания, кликабельные элементы, режимы выбора. Управление курсором осуществляется через DOM-слой canvas, поверх которого рендерится карта WebGL.
Основной точкой управления служит:
map.getCanvas().style.cursor
Этот параметр напрямую изменяет CSS-свойство cursor у
canvas-элемента карты.
Изменение курсора выполняется в любой момент жизненного цикла карты. Чаще всего используется связка событий наведения и ухода курсора с интерактивных слоёв.
map.getCanvas().style.cursor = 'default';
map.getCanvas().style.cursor = 'grab';
map.getCanvas().style.cursor = 'grabbing';
MapLibre GL JS предоставляет низкоуровневые события взаимодействия, которые позволяют динамически менять курсор в зависимости от положения указателя.
map.on('mouseenter', 'cities-layer', () => {
map.getCanvas().style.cursor = 'pointer';
});
map.on('mouseleave', 'cities-layer', () => {
map.getCanvas().style.cursor = '';
});
Использование пустой строки возвращает курсор к стилю по умолчанию.
В случаях, когда необходимо определять попадание курсора без привязки к конкретному слою, применяется метод:
map.queryRenderedFeatures(point, options)
Пример логики динамического изменения состояния:
map.on('mousemove', (e) => {
const features = map.queryRenderedFeatures(e.point);
if (features.length > 0) {
map.getCanvas().style.cursor = 'pointer';
} else {
map.getCanvas().style.cursor = '';
}
});
Такой подход применяется при сложных сценах с множеством перекрывающихся слоёв.
В интерфейсах карт часто требуется отражать несколько режимов взаимодействия.
map.on('mouseenter', 'pois', () => {
map.getCanvas().style.cursor = 'pointer';
});
map.on('mousedown', () => {
map.getCanvas().style.cursor = 'crosshair';
});
map.on('mouseup', () => {
map.getCanvas().style.cursor = '';
});
map.on('mousemove', (e) => {
if (editModeActive) {
map.getCanvas().style.cursor = 'cell';
}
});
MapLibre GL JS имеет встроенную возможность перетаскивания карты. Управление курсором часто синхронизируется с этими состояниями.
map.on('dragstart', () => {
map.getCanvas().style.cursor = 'grabbing';
});
map.on('dragend', () => {
map.getCanvas().style.cursor = 'grab';
});
При инициализации карты часто устанавливается базовое значение:
map.on('load', () => {
map.getCanvas().style.cursor = 'grab';
});
Современные браузеры поддерживают Pointer Events, которые объединяют мышь, сенсор и перо.
map.on('pointermove', (e) => {
const features = map.queryRenderedFeatures(e.point);
map.getCanvas().style.cursor = features.length ? 'pointer' : '';
});
Преимущество подхода заключается в универсальности для разных типов устройств ввода.
При сложных интерфейсах возможны конфликты между несколькими источниками управления курсором:
mouseenter,
mouseleave)mousemove,
pointermove)Для предотвращения конфликтов используется централизованная функция управления состоянием:
function setCursor(state) {
map.getCanvas().style.cursor = state;
}
И единая логика:
map.on('mousemove', (e) => {
if (editModeActive) {
setCursor('crosshair');
return;
}
const features = map.queryRenderedFeatures(e.point);
if (features.length) {
setCursor('pointer');
} else {
setCursor('');
}
});
При отрисовке кастомных слоёв (например, через
custom layer или canvas overlay) управление курсором
полностью выносится в прикладную логику.
canvas.addEventListener('mousemove', (e) => {
const hit = hitTestCustomLayer(e);
map.getCanvas().style.cursor = hit ? 'pointer' : 'default';
});
Такая схема применяется при интеграции с нестандартной графикой и WebGL-слоями.
При частых событиях mousemove или
pointermove изменение курсора может вызывать лишние
перерисовки DOM-свойств. Для оптимизации применяется проверка текущего
состояния:
let currentCursor = '';
function updateCursor(next) {
if (currentCursor !== next) {
currentCursor = next;
map.getCanvas().style.cursor = next;
}
}
Это уменьшает количество операций записи в DOM и повышает стабильность интерфейса при высокой частоте событий.
Чаще всего используются стандартные CSS-значения:
default — базовое состояниеpointer — интерактивный элементgrab — возможность перетаскиванияgrabbing — активное перетаскиваниеcrosshair — режим выбора/прицеливанияnot-allowed — запрещённое действиеwait — ожидание обработкиПри работе с визуальными слоями часто требуется согласование стиля слоя и поведения курсора:
map.addLayer({
id: 'buildings',
type: 'fill',
source: 'data',
paint: {
'fill-color': '#888'
}
});
map.on('mouseenter', 'buildings', () => {
map.getCanvas().style.cursor = 'pointer';
});
map.on('mouseleave', 'buildings', () => {
map.getCanvas().style.cursor = '';
});
Такой подход создаёт единое ощущение интерактивности между визуальным откликом слоя и поведением указателя.