fix: 修复预览面板拖拽分隔线快速移动时宽度不跟手的问题
根因:ResizeHandle 使用 document.addEventListener('mousemove') 监听拖拽,
当鼠标快速移到 iframe(HTML 预览)上方时,iframe 捕获了鼠标事件,
document 上的 mousemove 回调不再触发,导致宽度停止更新。
修复:将 mouse events 替换为 pointer events + setPointerCapture。
- onMouseDown → onPointerDown + setPointerCapture
- 在元素上直接监听 onPointerMove/onPointerUp(不再挂 document 事件)
- pointer capture 确保所有后续事件锁定到拖拽手柄,即使鼠标移到 iframe 上方
- 添加 touch-none CSS 防止触屏浏览器默认行为干扰拖拽
- 代码更简洁:不再需要手动 addEventListener/removeEventListener
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
056e908b72
commit
b1e6b0bfac
1 file changed
+31
-25
@@ -13,42 +13,48 @@ export function ResizeHandle({ side, onResize, onResizeEnd }: ResizeHandleProps)
|
||||
const isDragging = useRef(false);
|
||||
const startX = useRef(0);
|
||||
|
||||
const handleMouseDown = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
const handlePointerDown = useCallback(
|
||||
(e: React.PointerEvent) => {
|
||||
e.preventDefault();
|
||||
isDragging.current = true;
|
||||
startX.current = e.clientX;
|
||||
|
||||
// Capture pointer so all subsequent events route here, even over iframes
|
||||
(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);
|
||||
document.body.style.cursor = "col-resize";
|
||||
document.body.style.userSelect = "none";
|
||||
|
||||
const handleMouseMove = (ev: MouseEvent) => {
|
||||
if (!isDragging.current) return;
|
||||
const delta = ev.clientX - startX.current;
|
||||
startX.current = ev.clientX;
|
||||
onResize(delta);
|
||||
};
|
||||
|
||||
const handleMouseUp = () => {
|
||||
isDragging.current = false;
|
||||
document.body.style.cursor = "";
|
||||
document.body.style.userSelect = "";
|
||||
document.removeEventListener("mousemove", handleMouseMove);
|
||||
document.removeEventListener("mouseup", handleMouseUp);
|
||||
onResizeEnd?.();
|
||||
};
|
||||
|
||||
document.addEventListener("mousemove", handleMouseMove);
|
||||
document.addEventListener("mouseup", handleMouseUp);
|
||||
},
|
||||
[onResize, onResizeEnd]
|
||||
[]
|
||||
);
|
||||
|
||||
const handlePointerMove = useCallback(
|
||||
(e: React.PointerEvent) => {
|
||||
if (!isDragging.current) return;
|
||||
const delta = e.clientX - startX.current;
|
||||
startX.current = e.clientX;
|
||||
onResize(delta);
|
||||
},
|
||||
[onResize]
|
||||
);
|
||||
|
||||
const handlePointerUp = useCallback(
|
||||
(e: React.PointerEvent) => {
|
||||
if (!isDragging.current) return;
|
||||
isDragging.current = false;
|
||||
(e.currentTarget as HTMLElement).releasePointerCapture(e.pointerId);
|
||||
document.body.style.cursor = "";
|
||||
document.body.style.userSelect = "";
|
||||
onResizeEnd?.();
|
||||
},
|
||||
[onResizeEnd]
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
onMouseDown={handleMouseDown}
|
||||
onPointerDown={handlePointerDown}
|
||||
onPointerMove={handlePointerMove}
|
||||
onPointerUp={handlePointerUp}
|
||||
className={cn(
|
||||
"group relative z-10 flex w-1 shrink-0 cursor-col-resize items-center justify-center",
|
||||
"group relative z-10 flex w-1 shrink-0 cursor-col-resize items-center justify-center touch-none",
|
||||
side === "left" ? "-ml-0.5" : "-mr-0.5"
|
||||
)}
|
||||
>
|
||||
|
||||
Reference in new issue
Block a user