唯杰地图 logo唯杰地图
VJMAP
EN

mouse-keyboard

vjmap/references/interaction/mouse-keyboard.md 4.8 KB145 行
原文

本文回答:地图鼠标和键盘事件 —— click / mousemove / wheel / keydown。

一、vjmap 事件体系

map.on(type, handler) 可监听的事件如下(覆盖视图 / 鼠标 / 触摸 / 生命周期):

类别 事件
视图 move, movestart, moveend, zoom, zoomstart, zoomend, rotate, rotatestart, rotateend, pitch, pitchstart, pitchend
鼠标(地图级) click, dblclick, contextmenu, mousedown, mouseup, mousemove, mouseover, mouseout, wheel
鼠标(仅图层委托) mouseenter, mouseleave(mouseover/mouseout 也有图层形态)——必须 map.on(type, layerId, fn),不带图层不会触发
触摸 touchstart, touchend, touchmove, touchcancel
拖拽 dragstart, drag, dragend
框选 boxzoomstart, boxzoomend, boxzoomcancel
加载 load, idle, render, sourcedata, styledata, error
数据 data, dataloading, sourcedata, sourcedataloading, styledataloading, tiledataloading

二、带图层过滤的事件

map.on("click", "my-layer", (e) => {
  const feature = e.features[0];
  console.log(feature.properties);
});
map.on("mouseenter", "my-layer", () => map.getCanvas().style.cursor = "pointer");
map.on("mouseleave", "my-layer", () => map.getCanvas().style.cursor = "");

三、事件对象

字段 说明
e.lngLat { lng, lat } 经纬度
e.point { x, y } 容器像素
e.originalEvent 原生 MouseEvent / WheelEvent
e.features 命中的要素数组(带图层名过滤时才有)
e.type 事件名

四、CAD 坐标获取

map.on("click", (e) => {
  const lngLat = e.lngLat;
  const cad = map.fromLngLat(lngLat);   // {x, y} CAD 原始
});

五、鼠标穿透 / 禁用

map.on("click", (e) => {
  // 点击命中某图层就处理,否则返回
  const features = map.queryRenderedFeatures(e.point, { layers: ["my-layer"] });
  if (features.length === 0) return;
  // ...
});

六、键盘事件

  • vjmap 不派发地图级键盘事件:map.on("keydown" / "keyup", ...) 收不到真实按键(canvas 上的按键只交给内置键盘平移 / 缩放 handler)。
  • 监听真实按键用 DOM:map.getCanvas().focus(); map.getCanvasContainer().addEventListener("keydown", e => ...),或挂 window(见下)。
  • 模拟按键:map.fire("keyup", { keyCode: 13 })(程序化派发,map.on("keyup") 与 Draw 能收到;上面测量章节用过)。

绑定 window 全局(更可靠)

window.addEventListener("keydown", (e) => {
  if (e.key === "Escape") cancelCurrentAction();
});

七、双击 / 右键禁用

map.doubleClickZoom.disable();
map.on("contextmenu", () => {});   // 只要有 contextmenu 监听,引擎就屏蔽浏览器右键菜单;回调里 e.preventDefault() 不起这个作用

初始化时:

new vjmap.Map({ doubleClickZoom: false, ... });

八、查询命中要素

const features = map.queryRenderedFeatures(e.point, {
  layers: ["layer1", "layer2"],
  filter: ["==", ["get", "type"], "road"]
});

// 框选范围内
const features2 = map.queryRenderedFeatures([[x1, y1], [x2, y2]], { layers: [...] });

九、指针样式

map.getCanvas().style.cursor = "pointer";
map.getCanvas().style.cursor = "crosshair";
map.getCanvas().style.cursor = "";   // 还原

十、拖拽 / 点选冲突

按下到抬起位移 ≥ clickTolerance(默认 3px)时引擎本就不派发 click,拖图不会误触发 click。若仍需拖拽标志,必须在 mousedown 复位(否则拖拽后没有 click 来清标志,会吞掉下一次真正的点击):

let didDrag = false;
map.on("mousedown", () => didDrag = false);
map.on("dragstart", () => didDrag = true);
map.on("click", (e) => {
  if (didDrag) return;
  // 真正的 click
});

或用 map.on("mouseup", ...) + 判断起点与终点位移。

十一、Hover 防抖

高频 mousemove 回调要节流:

function throttle(fn, ms) {
  let last = 0;
  return (...args) => {
    const now = Date.now();
    if (now - last > ms) { last = now; fn(...args); }
  };
}
map.on("mousemove", throttle(e => {
  // 昂贵查询
}, 100));

十二、常见坑

  1. click 未 fire:canvas 上层有 div 遮挡。
  2. e.features 空:图层 id 错了 / 图层在 map.addLayer 之前。
  3. contextmenu 不触发:多半被上层 DOM 遮挡(同 click);用 map.on("contextmenu", ...) 而不是 window 的。
  4. map.on("keydown") 不 fire:地图不派发键盘事件,改用 DOM addEventListener(见 §六);DOM 监听挂在地图容器上时还需 canvas 聚焦。