问题
3D项目中使用Three.js设计模型,功能设计为右键旋转模型,所以直接禁用了右键菜单。
在3D场景上面,定位了些toolbar工具界面.
现在出现的问题是,在3D场景中,右键按下旋转模型,鼠标旋转时如果在工具界面,或者任务栏,松开右 键,会直接触发右键菜单。
在任务栏会触发任务栏设置,任务栏管理器等菜单。
过程
方法1 、添加全局右键e.button === 2菜单阻止 e.preventDefault();e.stopPropagation(); 。结果无效。
方法2 、在TrackballControls 控制器中监听右键的mousedown,mouseup, 禁用。 结果无效。
方法3、在Vue全局禁用右键,太偏激,放弃。
方法4、添加鼠标事件跟踪,跟踪右键按下状态 ,按下禁止,抬起时检测是否在canvas上。结果无效。
解决办法
核心解决方案:使用 setPointerCapture
setPointerCapture 可以将后续所有的鼠标/触控事件"锁定"在 Canvas 元素上,即使你的鼠标移出了 Canvas,移到了工具栏上,甚至移到了浏览器窗口之外(在大多数现代浏览器中),松开鼠标的事件依然会被 Canvas 接收,而不是被底下的元素接收。
javascript
function initHelpers() {
if (!camera.value || !renderer.domElement) {
console.error('初始化控制器失败:缺少相机或渲染器DOM元素');
return;
}
const canvas = renderer.domElement;
controls = new TrackballControls(camera.value, canvas);
controls.rotateSpeed = 7.0;
controls.zoomSpeed = 1.5;
controls.panSpeed = 1.0;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.1;
controls.target.set(75, 0, 0);
controls.update();
// 禁用控制器默认的右键逻辑
controls.mouseButtons = {
LEFT: THREE.MOUSE.NONE,
MIDDLE: THREE.MOUSE.PAN,
RIGHT: THREE.MOUSE.ROTATE,
};
// ============================================================
// 👇👇👇在这里 👇👇👇
// ============================================================
// 定义一个状态标记:是否正在(或刚刚结束)右键拖拽
let isRightDragging = false;
const handlePointerDown = (event) => {
if (event.button === 2) { // 右键
isRightDragging = true; // 标记开始
try {
canvas.setPointerCapture(event.pointerId); // 锁定鼠标
} catch (e) {
console.warn(e);
}
}
};
const handlePointerUp = (event) => {
if (event.button === 2) { // 右键
try {
canvas.releasePointerCapture(event.pointerId); // 释放锁定
} catch (e) {}
// 【关键点】:不要立即将 isRightDragging 设为 false
// 浏览器触发顺序通常是:mouseup -> contextmenu
// 我们延迟 100ms 重置状态,覆盖掉 contextmenu 触发的时间窗口
setTimeout(() => {
isRightDragging = false;
}, 100);
}
};
// 注册捕获阶段事件 (Capture Phase)
canvas.addEventListener('pointerdown', handlePointerDown, { capture: true });
canvas.addEventListener('pointerup', handlePointerUp, { capture: true });
// 全局拦截右键菜单
// 只要 isRightDragging 为 true (包含松开后的那100ms),就坚决禁止菜单
const preventGlobalContextMenu = (event) => {
if (isRightDragging) {
event.preventDefault();
event.stopPropagation(); // 阻止冒泡,防止上层 UI 接收到
return false;
}
};
window.addEventListener('contextmenu', preventGlobalContextMenu, { capture: true });
canvas.addEventListener('contextmenu', (e) => e.preventDefault());
// ============================================================
// 👆👆👆 代码结束 👆👆👆
// ============================================================
// View helper
helper = new ViewHelper(camera.value, renderer.domElement);
// ...
}
就这样。