fix:解决拖拽问题

This commit is contained in:
zhongyidian committed 2025-10-20 17:50:42 +08:00
1 parent ce06ad0214
commit 6367014e1f
34 files changed
+1712 -397

No files matched your search

+143
View File
@@ -0,0 +1,143 @@
import { useState, useEffect, useCallback, useRef } from "react";
import { useScale } from "@/contexts/ScaleContext";
interface DragState<T = any> {
isDragging: boolean;
dragData: T | null;
mousePosition: { x: number; y: number };
}
/**
* 自定义拖拽 Hook
* 支持页面 scale 缩放的拖拽功能
*/
export function useDragAndDrop<T = any>() {
const { scale, offsetX, offsetY } = useScale();
const [state, setState] = useState<DragState<T>>({
isDragging: false,
dragData: null,
mousePosition: { x: 0, y: 0 },
});
// 开始拖拽
const startDrag = useCallback((data: T, event?: React.MouseEvent) => {
if (event) {
event.preventDefault();
event.stopPropagation();
// 立即获取鼠标初始位置
setState({
isDragging: true,
dragData: data,
mousePosition: { x: event.clientX, y: event.clientY },
});
} else {
setState({
isDragging: true,
dragData: data,
mousePosition: { x: 0, y: 0 },
});
}
}, []);
// 结束拖拽
const endDrag = useCallback(() => {
setState({
isDragging: false,
dragData: null,
mousePosition: { x: 0, y: 0 },
});
}, []);
// 监听鼠标移动
useEffect(() => {
if (!state.isDragging) return;
const handleMouseMove = (e: MouseEvent) => {
setState((prev) => ({
...prev,
mousePosition: { x: e.clientX, y: e.clientY },
}));
};
const handleMouseUp = () => {
// 拖拽结束的逻辑由外部处理
// 这里只是提供一个安全机制
};
window.addEventListener("mousemove", handleMouseMove);
window.addEventListener("mouseup", handleMouseUp);
return () => {
window.removeEventListener("mousemove", handleMouseMove);
window.removeEventListener("mouseup", handleMouseUp);
};
}, [state.isDragging]);
// 将屏幕坐标转换为缩放后的页面坐标
const screenToScaledCoords = useCallback(
(screenX: number, screenY: number) => {
return {
x: (screenX - offsetX) / scale,
y: (screenY - offsetY) / scale,
};
},
[scale, offsetX, offsetY]
);
// 检查点是否在元素内
const isPointInElement = useCallback(
(pointX: number, pointY: number, element: HTMLElement | null): boolean => {
if (!element) return false;
const rect = element.getBoundingClientRect();
return (
pointX >= rect.left &&
pointX <= rect.right &&
pointY >= rect.top &&
pointY <= rect.bottom
);
},
[]
);
return {
isDragging: state.isDragging,
dragData: state.dragData,
mousePosition: state.mousePosition,
startDrag,
endDrag,
screenToScaledCoords,
isPointInElement,
};
}
/**
* 放置区域 Hook
* 用于检测拖拽元素是否悬停在放置区域上
*/
export function useDropZone() {
const dropZoneRef = useRef<HTMLDivElement>(null);
const [isOver, setIsOver] = useState(false);
// 检查鼠标是否在放置区域内
const checkIsOver = useCallback((mouseX: number, mouseY: number) => {
if (!dropZoneRef.current) return false;
const rect = dropZoneRef.current.getBoundingClientRect();
const over =
mouseX >= rect.left &&
mouseX <= rect.right &&
mouseY >= rect.top &&
mouseY <= rect.bottom;
setIsOver(over);
return over;
}, []);
return {
dropZoneRef,
isOver,
checkIsOver,
};
}
+86
View File
@@ -0,0 +1,86 @@
import { useEffect, useState, useMemo } from "react";
/**
* 等比例缩放Hook
* 基于1920*1080设计稿进行等比例缩放
*/
export function usePageScale() {
// 设计稿尺寸
const DESIGN_WIDTH = 1920;
const DESIGN_HEIGHT = 1080;
// 当前窗口尺寸
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
const [windowHeight, setWindowHeight] = useState(window.innerHeight);
// 缩放比例
const scale = useMemo(() => {
const scaleX = windowWidth / DESIGN_WIDTH;
const scaleY = windowHeight / DESIGN_HEIGHT;
// 取较小的缩放比例,确保内容完全显示在视窗内
return Math.min(scaleX, scaleY);
}, [windowWidth, windowHeight]);
// 缩放后的实际尺寸
const scaledWidth = useMemo(() => DESIGN_WIDTH * scale, [scale]);
const scaledHeight = useMemo(() => DESIGN_HEIGHT * scale, [scale]);
// 居中偏移量
const offsetX = useMemo(
() => (windowWidth - scaledWidth) / 2,
[windowWidth, scaledWidth]
);
const offsetY = useMemo(
() => (windowHeight - scaledHeight) / 2,
[windowHeight, scaledHeight]
);
// 缩放样式对象
const scaleStyle = useMemo(
() => ({
transform: `scale(${scale})`,
transformOrigin: "top left",
width: `${DESIGN_WIDTH}px`,
height: `${DESIGN_HEIGHT}px`,
position: "absolute" as const,
left: `${offsetX}px`,
top: `${offsetY}px`,
}),
[scale, offsetX, offsetY]
);
// 更新窗口尺寸
const updateWindowSize = () => {
setWindowWidth(window.innerWidth);
setWindowHeight(window.innerHeight);
};
// 监听窗口大小变化
useEffect(() => {
window.addEventListener("resize", updateWindowSize);
updateWindowSize();
return () => {
window.removeEventListener("resize", updateWindowSize);
};
}, []);
return {
// 响应式数据
windowWidth,
windowHeight,
scale,
scaledWidth,
scaledHeight,
offsetX,
offsetY,
// 样式对象
scaleStyle,
// 常量
DESIGN_WIDTH,
DESIGN_HEIGHT,
};
}