Перетаскивание объектов на карте в JavaScript API платформы HERE Technologies строится вокруг двух уровней взаимодействия: встроенных drag-событий карты и пользовательской реализации drag & drop через DOM и события браузера. В результате можно реализовать перемещение маркеров, интерактивное размещение объектов, перенос элементов интерфейса на карту и обратное преобразование экранных координат в географические.
В HERE Maps API маркеры поддерживают режим перетаскивания через
параметр volatility. Он включает механизм изменения
координат объекта при перемещении мыши или касании.
const platform = new H.service.Platform({
apikey: 'YOUR_API_KEY'
});
const defaultLayers = platform.createDefaultLayers();
const map = new H.Map(
document.getElementById('map'),
defaultLayers.vector.normal.map,
{
center: { lat: 52.5200, lng: 13.4050 },
zoom: 10,
pixelRatio: window.devicePixelRatio || 1
}
);
const beh * avior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
Создание перетаскиваемого маркера:
const marker = new H.map.Marker(
{ lat: 52.5200, lng: 13.4050 },
{
volatility: true
}
);
map.addObject(marker);
volatilityПеретаскивание маркеров сопровождается набором событий, которые позволяют контролировать процесс перемещения.
marker.addEventListener('dragstart', function (ev) {
console.log('Начало перетаскивания');
});
marker.addEventListener('drag', function (ev) {
const pointer = ev.currentPointer;
console.log(pointer.viewportX, pointer.viewportY);
});
marker.addEventListener('dragend', function (ev) {
const geo = map.screenToGeo(ev.currentPointer.viewportX, ev.currentPointer.viewportY);
console.log('Новая позиция:', geo);
});
dragstart — фиксирует начальную точку перемещенияdrag — вызывается при каждом движении курсораdragend — завершение перемещенияdragenter / dragleave — для сложных
сценариев взаимодействияКлючевая операция при drag & drop — перевод пикселей в координаты карты.
const geoPoint = map.screenToGeo(
pointer.viewportX,
pointer.viewportY
);
И обратное преобразование:
const screenPoint = map.geoToScreen(
{ lat: 52.52, lng: 13.405 }
);
Это позволяет:
Помимо встроенного drag маркеров, часто требуется перенос DOM-элементов (например, карточек объектов) на карту.
<div id="draggable" draggable="true">
Объект для переноса
</div>
const draggable = document.getElementById('draggable');
draggable.addEventListener('dragstart', function (ev) {
ev.dataTransfer.setData('text/plain', 'marker');
});
const mapContainer = map.getElement();
mapContainer.addEventListener('dragover', function (ev) {
ev.preventDefault();
});
mapContainer.addEventListener('drop', function (ev) {
ev.preventDefault();
const rect = mapContainer.getBoundingClientRect();
const viewportX = ev.clientX - rect.left;
const viewportY = ev.clientY - rect.top;
const geo = map.screenToGeo(viewportX, viewportY);
const newMarker = new H.map.Marker(geo);
map.addObject(newMarker);
});
При активном перетаскивании объектов необходимо учитывать конфликт с
перемещением самой карты. Для этого используется
Behavior.
const beh * avior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
Можно временно блокировать перемещение карты:
behavior.disable(H.mapevents.Behavior.DRAGGING);
И включать обратно:
behavior.enable(H.mapevents.Behavior.DRAGGING);
Перетаскивание не ограничивается маркерами. Любой объект
H.map.Object может быть расширен логикой drag.
const circle = new H.map.Circle(
{ lat: 52.52, lng: 13.405 },
500
);
circle.draggable = true;
map.addObject(circle);
Далее требуется обработка событий на уровне карты:
map.addEventListener('dragstart', function (ev) {
const target = ev.target;
if (target instanceof H.map.Circle) {
map.getViewModel().setLookAtData({
position: target.getCenter()
});
}
});
При сложных сценах необходимо определить, какой объект перетаскивается.
map.addEventListener('pointerdown', function (ev) {
const target = ev.target;
if (target && target.draggable) {
console.log('Начато перемещение объекта');
}
});
Иногда требуется сохранять смещение курсора относительно объекта:
let offset = { x: 0, y: 0 };
map.addEventListener('dragstart', function (ev) {
const pointer = ev.currentPointer;
const target = ev.target;
const screen = map.geoToScreen(target.getGeometry());
offset.x = pointer.viewportX - screen.x;
offset.y = pointer.viewportY - screen.y;
});
При перемещении:
map.addEventListener('drag', function (ev) {
const pointer = ev.currentPointer;
const x = pointer.viewportX - offset.x;
const y = pointer.viewportY - offset.y;
const geo = map.screenToGeo(x, y);
ev.target.setGeometry(geo);
});
При массовом перемещении используется коллекция
H.map.Group.
const group = new H.map.Group();
group.addObjects([
new H.map.Marker({ lat: 52.52, lng: 13.4 }),
new H.map.Marker({ lat: 52.53, lng: 13.41 })
]);
map.addObject(group);
Обработка перемещения:
group.addEventListener('drag', function (ev) {
const target = ev.target;
if (target instanceof H.map.Marker) {
const pointer = ev.currentPointer;
const geo = map.screenToGeo(pointer.viewportX, pointer.viewportY);
target.setGeometry(geo);
}
});
Для контроля перемещения можно ограничивать координаты:
function clampGeo(geo) {
return {
lat: Math.max(-85, Math.min(85, geo.lat)),
lng: Math.max(-180, Math.min(180, geo.lng))
};
}
Применение:
map.addEventListener('drag', function (ev) {
const pointer = ev.currentPointer;
const geo = map.screenToGeo(pointer.viewportX, pointer.viewportY);
ev.target.setGeometry(clampGeo(geo));
});
Перетаскивание маркеров для точной установки POI с сохранением координат в базе данных.
Перемещение точек доставки с автоматическим пересчетом маршрута.
Перетаскивание границ зон на карте с обновлением геометрии полигона.
Добавление объектов из интерфейса на карту через drag DOM-элементов с последующей геокодировкой.
При интенсивном перемещении объектов важно снижать нагрузку:
drag через throttlerequestAnimationFrame для синхронизации
обновленийlet lastUpdate = 0;
map.addEventListener('drag', function (ev) {
const now = performance.now();
if (now - lastUpdate < 16) return;
lastUpdate = now;
const geo = map.screenToGeo(
ev.currentPointer.viewportX,
ev.currentPointer.viewportY
);
ev.target.setGeometry(geo);
});