fix:解决拖拽问题
This commit is contained in:
1 parent
ce06ad0214
commit
6367014e1f
34 files changed
+1712
-397
No files matched your search
@@ -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,
|
||||
};
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user