map-interaction
本文回答:
map.on鼠标 / 触摸 / 键盘事件 —— click / mousemove / wheel / contextmenu / touch。
一、鼠标事件列表
| 事件 | 说明 |
|---|---|
click |
单击 |
dblclick |
双击 |
contextmenu |
右键 |
mousedown / mouseup |
按下 / 抬起 |
mousemove |
移动(高频) |
mouseover / mouseout |
进入 / 离开 canvas |
mouseenter / mouseleave |
仅 map.on(type, layerId, fn) 有效(进入 / 离开该图层要素);不带 layerId 永不触发 |
wheel |
滚轮 |
dragstart / drag / dragend |
拖拽 |
二、事件对象结构
interface MapMouseEvent {
type: string;
lngLat: { lng, lat };
point: { x, y }; // 容器像素
originalEvent: MouseEvent;
features?: Feature[]; // 带图层过滤时才有
preventDefault(): void;
}
CAD 坐标:
map.on('click', e => {
const lngLat = e.lngLat;
const cad = map.fromLngLat(lngLat); // {x, y}
});
三、带图层过滤
map.on('click', 'my-layer', e => {
console.log(e.features[0].properties);
});
map.on('mouseenter', 'my-layer', () => map.getCanvas().style.cursor = 'pointer');
map.on('mouseleave', 'my-layer', () => map.getCanvas().style.cursor = '');
图层级事件只在命中该图层要素时触发。
四、触摸事件
| 事件 | 说明 |
|---|---|
touchstart / touchmove / touchend / touchcancel |
触摸 |
map.on('touchstart', e => console.log(e.points)); // 多指时 points 是数组
五、键盘事件
vjmap 不派发地图级键盘事件:map.on('keydown' / 'keyup') 收不到真实按键(canvas 上的按键只交给内置键盘平移 / 缩放 handler;仅 map.fire('keyup', …) 程序化派发能触发)。用 DOM 监听:
map.getCanvas().focus(); // canvas 获得焦点后按键才会落到地图容器上
map.getCanvasContainer().addEventListener('keydown', e => console.log(e.key));
// 或全局:window.addEventListener('keydown', e => ...)
六、右键菜单
只要有 map.on('contextmenu') 监听(或 dragRotate 处于启用状态),引擎自动对原生事件调 preventDefault,浏览器菜单不会弹出:
map.on('contextmenu', e => {
// 无需 e.preventDefault():它只给内部 handler 打标记,不是用来防浏览器菜单的
showMyMenu(e.point.x, e.point.y);
});
右键拖拽默认是旋转 / 俯仰(不是平移),禁用:
map.dragRotate.disable();
七、滚轮 / 缩放
map.on('wheel', e => {
// e.originalEvent.deltaY
});
map.on('zoom', () => console.log(map.getZoom()));
禁用滚轮缩放:
map.scrollZoom.disable();
八、拖拽识别
按下到抬起位移 ≥ 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.getCanvas().style.cursor = 'pointer';
map.getCanvas().style.cursor = 'crosshair';
map.getCanvas().style.cursor = '';
十、节流高频事件
mousemove 每秒 60+ 次:
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));
或用 requestAnimationFrame 合并:
let pending = false;
map.on('mousemove', e => {
if (pending) return;
pending = true;
requestAnimationFrame(() => {
// 处理
pending = false;
});
});
十一、常见坑
- click 不 fire:canvas 上层有 DOM 挡住。
- e.features 空:图层 id 错 / 图层未 add。
- contextmenu 不 fire:多半被上层 DOM 遮挡(同 click)。
map.on('keydown')不 fire:地图不派发键盘事件,改用 DOMaddEventListener(见 §五)。- 高频 mousemove 卡顿:必节流。
- addControl 前绑定事件 → 有时候事件 ordering 导致控件先接管;尽量 await
map.onLoad()后再绑。
相关
- 生命周期 → map-lifecycle
- 图层级事件 → layer-events
- 节流 / RAF → perf/event-throttling