fix: 优化右键拖动布局

This commit is contained in:
2026-09-18 03:16:57 +08:00
parent 9b72f10b43
commit 2280775e8d

View File

@@ -10,7 +10,7 @@ import {
type CSSProperties,
type MutableRefObject,
} from "react";
import { Euler, Matrix4, PerspectiveCamera as ThreePerspectiveCamera, Quaternion, Spherical, Vector3 } from "three";
import { Euler, Matrix4, MOUSE, PerspectiveCamera as ThreePerspectiveCamera, Quaternion, Spherical, Vector3 } from "three";
import type { Object3D } from "three";
import type { OrbitControls as OrbitControlsImpl } from "three-stdlib";
import { clearViewportCaptureHandler, setViewportCaptureHandler } from "../io/captureBridge";
@@ -640,6 +640,40 @@ function ViewportWheelGuard({ enabled }: { enabled: boolean }) {
return null;
}
/** 兜底:拦截画布上的浏览器右键上下文菜单(平移已改用中键,这里主要防误触右键弹系统菜单)。
* 以捕获阶段 + 非 passive 挂在 canvas 原生 dom 上;对右键 pointerdown 也 preventDefault,
* 顺手挡掉部分浏览器仍会冒出来的右键拖拽默认行为。 */
function ViewportContextMenuGuard() {
const { gl } = useThree();
useEffect(() => {
const canvas = gl.domElement;
if (typeof canvas?.addEventListener !== "function") {
return;
}
const handleContextMenu = (event: Event) => {
event.preventDefault();
};
// 右键按下时先拦掉浏览器默认行为,避免 Edge 把右键拖拽识别成手势
const handlePointerDown = (event: PointerEvent) => {
if (event.button === 2) {
event.preventDefault();
}
};
// 捕获阶段、非 passive:确保先于任何浏览器默认/手势逻辑执行
canvas.addEventListener("contextmenu", handleContextMenu, { capture: true, passive: false });
canvas.addEventListener("pointerdown", handlePointerDown, { capture: true, passive: false });
canvas.addEventListener("dragstart", handleContextMenu, { capture: true, passive: false });
return () => {
canvas.removeEventListener("contextmenu", handleContextMenu, { capture: true });
canvas.removeEventListener("pointerdown", handlePointerDown, { capture: true });
canvas.removeEventListener("dragstart", handleContextMenu, { capture: true });
};
}, [gl]);
return null;
}
/** 把渲染三件套注册给视频导出逻辑(逐帧离线渲染用),模式仿 CanvasCaptureBridge */
function VideoExportBridge() {
const { camera, gl, scene } = useThree();
@@ -914,8 +948,8 @@ export function DirectorCanvas() {
<div
className={`director-canvas${trajectoryPlacementObjectId ? " is-placing-trajectory" : ""}`}
data-testid="director-canvas"
// 屏蔽在画布上右键(右键平移)弹出的浏览器上下文菜单 / 手势:
// 相机视角下没有 OrbitControls 接管右键,右键拖拽会直接触发浏览器手势,这里统一拦截。
// 兜底:拦截画布上的浏览器右键上下文菜单(与平移无关——平移已改中键)。
// 相机视角下没有 OrbitControls 接管,右键仍可能弹出系统菜单,这里统一拦掉。
onContextMenu={(event) => event.preventDefault()}
>
<Canvas
@@ -971,6 +1005,11 @@ export function DirectorCanvas() {
enabled
makeDefault
target={DEFAULT_DIRECTOR_VIEW_SNAPSHOT.target}
// 按键映射:左键旋转 / 中键平移 / 滚轮推拉。
// 故意不把"平移"绑到右键——Edge 自 121 版起把「按住右键拖拽」作为浏览器级鼠标手势
// (前进/后退/切标签),页面层 preventDefault 无法拦截(微软官方确认无 JS 方案)。
// 平移改用中键,触摸板用户用双指拖拽即可,右键不再触发手势。
mouseButtons={{ LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.PAN, RIGHT: undefined }}
// 滚轮推拉距离:three 默认 1 偏慢,这里整体调快(见常量注释)
zoomSpeed={VIEWPORT_WHEEL_ZOOM_SPEED}
onChange={(event) => {
@@ -1024,6 +1063,7 @@ export function DirectorCanvas() {
viewMode={viewMode}
/>
<ViewportWheelGuard enabled={viewMode === "camera"} />
<ViewportContextMenuGuard />
<PlaybackFrameDriver />
<VideoExportBridge />
<PlaybackTransformSync />