Leaflet поддерживает интерактивное перемещение объектов карты через механизм drag-and-drop, встроенный в слой маркеров. Перетаскивание маркеров является одним из ключевых инструментов построения интерактивных картографических интерфейсов: оно позволяет пользователю изменять географическое положение объектов в реальном времени и синхронизировать эти изменения с бизнес-логикой приложения.
По умолчанию маркеры в Leaflet могут быть перетаскиваемыми только при явном включении соответствующего режима. Основной параметр задаётся при создании маркера:
const marker = L.marker([51.505, -0.09], {
draggable: true
}).addTo(map);
Ключевое свойство draggable: true активирует обработчики
событий мыши и сенсорного ввода, позволяя изменять координаты маркера
непосредственно на карте.
Альтернативный способ — включение перетаскивания после создания объекта:
marker.dragging.enable();
Отключение выполняется аналогично:
marker.dragging.disable();
Такой подход используется в сценариях, где режим редактирования карты переключается динамически.
Механизм drag в Leaflet построен на событийной модели. При перемещении маркера генерируются следующие события:
dragstart — начало перетаскиванияdrag — процесс перемещенияdragend — завершение перемещенияПример подписки:
marker.on('dragstart', function () {
console.log('Перетаскивание началось');
});
marker.on('drag', function () {
const position = marker.getLatLng();
console.log('Текущие координаты:', position);
});
marker.on('dragend', function () {
const finalPosition = marker.getLatLng();
console.log('Финальные координаты:', finalPosition);
});
События позволяют синхронизировать состояние маркера с сервером, формами или другими слоями карты.
Во время и после перетаскивания координаты маркера извлекаются
методом getLatLng():
const coords = marker.getLatLng();
console.log(coords.lat, coords.lng);
Для программного изменения позиции используется
setLatLng():
marker.setLatLng([51.51, -0.1]);
Этот метод часто применяется после валидации пользовательского ввода или при восстановлении состояния карты.
При перемещении маркера за пределы текущего viewport карта может
оставаться статичной. Для автоматического следования за маркером
используется обработка события drag:
marker.on('drag', function () {
map.panTo(marker.getLatLng());
});
Такой подход полезен в интерфейсах, где маркер представляет активную сущность (например, курьер или транспорт).
Более плавный вариант:
map.panTo(marker.getLatLng(), { animate: true });
Leaflet позволяет ограничивать перемещение маркеров географическими границами. Это реализуется через проверку координат в обработчике события:
const bounds = L.latLngBounds(
L.latLng(51.49, -0.08),
L.latLng(51.52, -0.06)
);
marker.on('drag', function () {
const pos = marker.getLatLng();
if (!bounds.contains(pos)) {
marker.setLatLng(bounds.getCenter());
}
});
Более мягкий вариант — «возврат» маркера в допустимую область после завершения перетаскивания:
marker.on('dragend', function () {
const pos = marker.getLatLng();
if (!bounds.contains(pos)) {
marker.setLatLng(bounds.getCenter());
}
});
В задачах GIS и логистики часто требуется привязка маркера к дискретной сетке координат. Это реализуется округлением значений:
function snap(value, step) {
return Math.round(value / step) * step;
}
marker.on('drag', function () {
const pos = marker.getLatLng();
const snapped = L.latLng(
snap(pos.lat, 0.001),
snap(pos.lng, 0.001)
);
marker.setLatLng(snapped);
});
Такой механизм уменьшает шум координат и обеспечивает стандартизированное позиционирование.
Перетаскивание маркера часто используется вместе с обновлением других элементов интерфейса, например линий или полигонов.
Пример обновления линии:
const polyline = L.polyline([
[51.505, -0.09],
[51.51, -0.1]
]).addTo(map);
marker.on('drag', function () {
const pos = marker.getLatLng();
const latlngs = polyline.getLatLngs();
latlngs[0] = pos;
polyline.setLatLngs(latlngs);
});
Это позволяет строить интерактивные редакторы маршрутов.
При наличии группы перетаскиваемых маркеров важно централизовать обработку событий:
function onMarkerDrag(e) {
const marker = e.target;
const pos = marker.getLatLng();
console.log('Маркер перемещён:', pos);
}
markers.forEach(m => {
m.on('dragend', onMarkerDrag);
});
Такой подход упрощает масштабирование логики при большом количестве объектов.
Событие drag может вызываться десятки раз в секунду. Для
оптимизации вычислений используется throttling:
let lastUpdate = 0;
marker.on('drag', function () {
const now = Date.now();
if (now - lastUpdate < 50) return;
lastUpdate = now;
const pos = marker.getLatLng();
console.log(pos);
});
Это снижает нагрузку на DOM и предотвращает лишние вычисления.
В реальных приложениях изменение координат часто отправляется на сервер:
marker.on('dragend', async function () {
const pos = marker.getLatLng();
await fetch('/api/update-position', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
lat: pos.lat,
lng: pos.lng
})
});
});
Отправка данных именно в dragend уменьшает количество
сетевых запросов и повышает стабильность интерфейса.
Перетаскивание может сопровождаться обновлением popup:
marker.bindPopup('Переместите маркер');
marker.on('drag', function () {
const pos = marker.getLatLng();
marker.setPopupContent(`lat: ${pos.lat.toFixed(5)}, lng: ${pos.lng.toFixed(5)}`);
});
Popup становится динамическим индикатором координат.
Перетаскивание не зависит от типа иконки:
const customIcon = L.icon({
iconUrl: 'icon.png',
iconSize: [32, 32]
});
const marker = L.marker([51.505, -0.09], {
icon: customIcon,
draggable: true
}).addTo(map);
Это позволяет создавать интерактивные слои с визуально сложными маркерами без потери функциональности drag.
Маркер может управляться кнопками интерфейса:
document.querySelector('#enable').oncl ick = () => marker.dragging.enable();
document.querySelector('#disable').oncl ick = () => marker.dragging.disable();
Такой подход используется в режимах редактирования, где пользователь явно включает изменение геометрии объектов.
Перетаскивание часто связывается с input-полями:
marker.on('drag', function () {
const pos = marker.getLatLng();
document.querySelector('#lat').value = pos.lat;
document.querySelector('#lng').value = pos.lng;
});
Обратная синхронизация:
function updateMarker() {
marker.setLatLng([
parseFloat(document.querySelector('#lat').value),
parseFloat(document.querySelector('#lng').value)
]);
}
Такая двусторонняя связь используется в административных панелях геоданных.