唯杰地图 logo唯杰地图
VJMAP
EN

events

vjmap/references/marker/events.md 5.3 KB147 行
原文

本文回答:Marker 事件 —— click / mouseover / mouseleave / contextmenu / dragstart / drag / dragend / autoAdd / autoRemove。

一、Marker 本身的事件(marker.on)

事件 触发时机
dragstart 拖拽开始
drag 拖拽中(频繁触发)
dragend 拖拽结束
autoAdd 自动插回 DOM(removeWhenNoInMapView 回到视野,或 minZoom/maxZoom 回到区间)
autoRemove 自动摘除 DOM(离开视野或超出缩放区间)
click / dblclick / contextmenu / mousedown / mouseup / mousemove / mouseover / mouseleave / touchstart / touchmove / touchend element 上对应 DOM 事件的转发(hover 用 mouseover,没有 mouseenter)
marker.on("dragend", e => { /* e.target 即被拖动的 marker(事件是裸 Event,无 e.marker 字段) */ });
marker.on("autoAdd", () => console.log("进入视野"));
marker.on("autoRemove", () => console.log("离开视野"));

marker.on("click") 等转发事件的参数是把原生 DOM 事件字段逐个复制出来的普通对象:e.clientX / e.button 等可读,e.target 是 marker 实例;没有 originalEvent / lngLat。复制来的 e.stopPropagation() / e.preventDefault() 一调就抛 Illegal invocation——要阻止冒泡用 §二的 element DOM 监听。

二、DOM 事件(通过 element)

Marker 的 click / hover 通过 DOM 监听:

const el = marker.getElement();
el.addEventListener("click", e => {
  e.stopPropagation();   // 阻止冒泡到地图
  alert("Marker clicked");
});
el.addEventListener("mouseenter", () => {
  el.style.background = "#ff0";
});
el.addEventListener("mouseleave", () => {
  el.style.background = "";
});
el.addEventListener("contextmenu", e => {
  e.preventDefault();
  alert("右键");
});

三、click 传递经纬度

const el = marker.getElement();
el.addEventListener("click", () => {
  const ll = marker.getLngLat();
  alert(`点击 (${ll.lng}, ${ll.lat})`);
});

四、hover 联动 Popup

const popup = new vjmap.Popup({ closeButton: false, offset: [0, -40] })
  .setHTML("<b>站点 A</b><br/>状态:正常");

const el = marker.getElement();
el.addEventListener("mouseenter", () => {
  marker.setPopup(popup);
  popup.addTo(map);
  popup.setLngLat(marker.getLngLat());
});
el.addEventListener("mouseleave", () => popup.remove());

或直接绑定:

marker.setPopup(popup);
marker.togglePopup();   // 点 Marker 切换

五、批量 Marker 事件委托

如果 DOM 都有统一父元素,可用事件委托:

map.getCanvasContainer().addEventListener("click", e => {
  const el = e.target.closest(".my-marker");   // DOM 元素,非 Marker 实例
  if (el) {
    const id = el.dataset.id;
    console.log("点击 marker", id);
  }
});

Marker 元素就挂在 map.getCanvasContainer() 下(与 canvas 同级),所以委托到 canvasContainer 有效;用 closest 过滤出 Marker 节点即可。

六、拖拽 + 点击防抖

let isDragging = false;
marker.on("dragstart", () => { isDragging = true; });
marker.on("dragend",   () => setTimeout(() => { isDragging = false; }, 50));

marker.getElement().addEventListener("click", e => {
  if (isDragging) return;
  alert("click");
});

七、autoAdd / autoRemove(视野裁剪事件)

const m = vjmap.createMarker({ removeWhenNoInMapView: true })  // 扩展字段须走 createMarker(或 setRemoveWhenNoInMapView)
  .setLngLat(lngLat);
m.on("autoAdd", () => console.log("进入视野"));
m.on("autoRemove", () => console.log("离开视野"));
m.addTo(map);   // addTo 返回 undefined,不能在其后接 .on(...)

场景:把进入视野的 Marker 数量写入统计 / UI 显示。

八、监听缩放级别触发的 show/hide

map.on("zoomend", () => {
  const z = map.getZoom();
  marker.getElement().style.opacity = z > 15 ? 1 : 0.3;
});

九、配合地图 click 做反向 hitTest

map.on("click", e => {
  // 计算最近的 Marker(注意:Marker 没有 id 属性,自己维护 id→marker 映射)
  let nearest = null, nearestId = null, minDist = Infinity;
  for (const [id, m] of markerMap) {
    const d = distance(e.lngLat, m.getLngLat());
    if (d < minDist) { minDist = d; nearest = m; nearestId = id; }
  }
  if (minDist < 100) alert("最近的 Marker id=" + nearestId);
});

十、常见坑

  1. click 未触发 → CSS pointer-events: none 让 Marker 不接收事件;改为 auto。
  2. click 同时触发了 map click → e.stopPropagation() 在 Marker element 的 click 里。
  3. hover 闪烁 → 检查 closeOnMove:默认 false;若显式开了,地图 pan/zoom 时会关 popup(不是鼠标移动),hover 联动场景保持默认即可。
  4. autoRemove 不丢监听:autoRemove 只是把同一 DOM 节点从文档摘除、autoAdd 再插回,自己 addEventListener 的监听和 marker.on 的订阅都还在,无需重绑(注意:autoRemove 会顺带关闭绑定的 popup)。
  5. Vue/React 里把 click handler 写成闭包:每次渲染都重建函数,建议用 useCallback / 稳定引用。