diff --git a/src/app/page.tsx b/src/app/page.tsx index 9b37dc3..a94c3e2 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -174,7 +174,13 @@ export default function Home() { // 放大镜切换处理函数 const handleToggleMagnifier = () => { - setIsMagnifierActive(!isMagnifierActive); + const newActiveState = !isMagnifierActive; + setIsMagnifierActive(newActiveState); + + // 如果关闭放大镜,清除选择区域,重新开始 + if (!newActiveState) { + setMagnifierSelectionArea(null); + } }; // 放大镜像素编辑处理函数 @@ -2141,6 +2147,7 @@ export default function Home() { }); setHighlightColorKey(null); setIsMagnifierActive(false); + setMagnifierSelectionArea(null); }} onToggleMagnifier={handleToggleMagnifier} isMagnifierActive={isMagnifierActive} diff --git a/src/components/FloatingColorPalette.tsx b/src/components/FloatingColorPalette.tsx index ddc3d56..f2bc8c6 100644 --- a/src/components/FloatingColorPalette.tsx +++ b/src/components/FloatingColorPalette.tsx @@ -85,10 +85,14 @@ const FloatingColorPalette: React.FC = ({ }; const handleMouseMove = (e: MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); handleMove(e.clientX, e.clientY); }; const handleTouchMove = (e: TouchEvent) => { + e.preventDefault(); + e.stopPropagation(); if (e.touches.length > 0) { handleMove(e.touches[0].clientX, e.touches[0].clientY); } @@ -96,9 +100,14 @@ const FloatingColorPalette: React.FC = ({ const handleEnd = () => { setIsDragging(false); + // 恢复页面滚动 + document.body.style.overflow = ''; }; if (isDragging) { + // 阻止页面滚动 + document.body.style.overflow = 'hidden'; + document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseup', handleEnd); document.addEventListener('touchmove', handleTouchMove, { passive: false }); @@ -109,6 +118,8 @@ const FloatingColorPalette: React.FC = ({ document.removeEventListener('mouseup', handleEnd); document.removeEventListener('touchmove', handleTouchMove); document.removeEventListener('touchend', handleEnd); + // 清理时恢复滚动 + document.body.style.overflow = ''; }; } }, [isDragging, dragOffset]); diff --git a/src/components/MagnifierSelectionOverlay.tsx b/src/components/MagnifierSelectionOverlay.tsx index 836f479..2c3c4d5 100644 --- a/src/components/MagnifierSelectionOverlay.tsx +++ b/src/components/MagnifierSelectionOverlay.tsx @@ -68,14 +68,24 @@ const MagnifierSelectionOverlay: React.FC = ({ // 不再强制恢复滚动位置,让浏览器保持自然状态 }, [preventScrolling]); - // 获取画布相对坐标 + // 获取画布相对坐标 - 优化移动设备支持 const getCanvasCoordinates = useCallback((clientX: number, clientY: number) => { if (!canvasRef.current) return null; - const rect = canvasRef.current.getBoundingClientRect(); + const canvas = canvasRef.current; + const rect = canvas.getBoundingClientRect(); + + // 考虑设备像素比和画布缩放 + const scaleX = canvas.width / rect.width; + const scaleY = canvas.height / rect.height; + + // 计算相对于画布的坐标 + const x = (clientX - rect.left) * scaleX; + const y = (clientY - rect.top) * scaleY; + return { - x: clientX - rect.left, - y: clientY - rect.top + x: Math.max(0, Math.min(canvas.width, x)), + y: Math.max(0, Math.min(canvas.height, y)) }; }, [canvasRef]); @@ -150,7 +160,13 @@ const MagnifierSelectionOverlay: React.FC = ({ const handleTouchStart = useCallback((event: React.TouchEvent) => { if (!isActive) return; + // 先阻止默认行为,避免滚动干扰 + event.preventDefault(); + event.stopPropagation(); + const touch = event.touches[0]; + if (!touch) return; + const coords = getCanvasCoordinates(touch.clientX, touch.clientY); if (!coords) return; @@ -158,18 +174,22 @@ const MagnifierSelectionOverlay: React.FC = ({ setSelectionStart(coords); setSelectionEnd(coords); disableScroll(); // 禁用滚动 - event.preventDefault(); }, [isActive, getCanvasCoordinates, disableScroll]); const handleTouchMove = useCallback((event: TouchEvent) => { if (!isSelecting || !selectionStart) return; + // 先阻止默认行为 + event.preventDefault(); + event.stopPropagation(); + const touch = event.touches[0]; + if (!touch) return; + const coords = getCanvasCoordinates(touch.clientX, touch.clientY); if (!coords) return; setSelectionEnd(coords); - event.preventDefault(); }, [isSelecting, selectionStart, getCanvasCoordinates]); const handleTouchEnd = useCallback(() => { @@ -215,11 +235,22 @@ const MagnifierSelectionOverlay: React.FC = ({ const getSelectionStyle = useCallback(() => { if (!selectionStart || !selectionEnd || !canvasRef.current) return {}; - const rect = canvasRef.current.getBoundingClientRect(); - const minX = Math.min(selectionStart.x, selectionEnd.x); - const minY = Math.min(selectionStart.y, selectionEnd.y); - const maxX = Math.max(selectionStart.x, selectionEnd.x); - const maxY = Math.max(selectionStart.y, selectionEnd.y); + const canvas = canvasRef.current; + const rect = canvas.getBoundingClientRect(); + + // 将画布坐标转换回屏幕坐标 + const scaleX = rect.width / canvas.width; + const scaleY = rect.height / canvas.height; + + const screenStartX = selectionStart.x * scaleX; + const screenStartY = selectionStart.y * scaleY; + const screenEndX = selectionEnd.x * scaleX; + const screenEndY = selectionEnd.y * scaleY; + + const minX = Math.min(screenStartX, screenEndX); + const minY = Math.min(screenStartY, screenEndY); + const maxX = Math.max(screenStartX, screenEndX); + const maxY = Math.max(screenStartY, screenEndY); return { left: rect.left + minX, diff --git a/src/components/MagnifierTool.tsx b/src/components/MagnifierTool.tsx index bf4f073..29123dc 100644 --- a/src/components/MagnifierTool.tsx +++ b/src/components/MagnifierTool.tsx @@ -32,8 +32,15 @@ const MagnifierTool: React.FC = ({ selectionArea, onClearSelection }) => { - const [magnifierPosition, setMagnifierPosition] = useState<{ x: number; y: number }>({ x: 50, y: 50 }); + // 计算初始位置,确保在屏幕中央 + const getInitialPosition = () => ({ + x: Math.max(50, (window.innerWidth - 400) / 2), + y: Math.max(50, (window.innerHeight - 400) / 2) + }); + + const [magnifierPosition, setMagnifierPosition] = useState<{ x: number; y: number }>(getInitialPosition); const [isDragging, setIsDragging] = useState(false); + const [dragOffset, setDragOffset] = useState<{ x: number; y: number }>({ x: 0, y: 0 }); const magnifierRef = useRef(null); const canvasRef = useRef(null); @@ -62,14 +69,9 @@ const MagnifierTool: React.FC = ({ canvas.width = width * magnifiedCellSize; canvas.height = height * magnifiedCellSize; - // 设置画布的显示尺寸,确保不会太大 - const maxDisplayWidth = 400; - const maxDisplayHeight = 400; - const displayWidth = Math.min(canvas.width, maxDisplayWidth); - const displayHeight = Math.min(canvas.height, maxDisplayHeight); - - canvas.style.width = `${displayWidth}px`; - canvas.style.height = `${displayHeight}px`; + // 保持真实尺寸,不压缩 + canvas.style.width = `${canvas.width}px`; + canvas.style.height = `${canvas.height}px`; // 清空画布 ctx.clearRect(0, 0, canvas.width, canvas.height); @@ -141,7 +143,7 @@ const MagnifierTool: React.FC = ({ } }, [selectionArea, mappedPixelData, selectedColor, onPixelEdit]); - // 处理拖拽移动 + // 处理拖拽移动 - 鼠标事件 const handleTitleBarMouseDown = useCallback((event: React.MouseEvent) => { // 只有点击在标题栏区域且不是按钮时才开始拖拽 const target = event.target as HTMLElement; @@ -149,33 +151,101 @@ const MagnifierTool: React.FC = ({ return; // 点击按钮时不拖拽 } + if (magnifierRef.current) { + const rect = magnifierRef.current.getBoundingClientRect(); + // 记录鼠标相对于窗口左上角的偏移 + setDragOffset({ + x: event.clientX - rect.left, + y: event.clientY - rect.top + }); + } + setIsDragging(true); + // 阻止页面滚动 + document.body.style.overflow = 'hidden'; + event.preventDefault(); + }, []); + + // 处理拖拽移动 - 触摸事件 + const handleTitleBarTouchStart = useCallback((event: React.TouchEvent) => { + // 只有点击在标题栏区域且不是按钮时才开始拖拽 + const target = event.target as HTMLElement; + if (target.tagName === 'BUTTON' || target.closest('button')) { + return; // 点击按钮时不拖拽 + } + + const touch = event.touches[0]; + if (!touch) return; + + if (magnifierRef.current) { + const rect = magnifierRef.current.getBoundingClientRect(); + // 记录触摸相对于窗口左上角的偏移 + setDragOffset({ + x: touch.clientX - rect.left, + y: touch.clientY - rect.top + }); + } + + setIsDragging(true); + // 阻止页面滚动 + document.body.style.overflow = 'hidden'; event.preventDefault(); }, []); const handleMouseMove = useCallback((event: MouseEvent) => { - if (isDragging && magnifierRef.current) { - const rect = magnifierRef.current.getBoundingClientRect(); - const newX = Math.max(0, Math.min(window.innerWidth - rect.width, event.clientX - rect.width / 2)); - const newY = Math.max(0, Math.min(window.innerHeight - rect.height, event.clientY - rect.height / 2)); + if (isDragging) { + event.preventDefault(); + event.stopPropagation(); + // 计算新位置,保持鼠标相对于窗口的偏移不变,不限制边界 + const newX = event.clientX - dragOffset.x; + const newY = event.clientY - dragOffset.y; setMagnifierPosition({ x: newX, y: newY }); } - }, [isDragging]); + }, [isDragging, dragOffset]); + + const handleTouchMove = useCallback((event: TouchEvent) => { + if (isDragging) { + event.preventDefault(); + event.stopPropagation(); + const touch = event.touches[0]; + if (!touch) return; + + // 计算新位置,保持触摸相对于窗口的偏移不变,不限制边界 + const newX = touch.clientX - dragOffset.x; + const newY = touch.clientY - dragOffset.y; + setMagnifierPosition({ x: newX, y: newY }); + } + }, [isDragging, dragOffset]); const handleMouseUp = useCallback(() => { setIsDragging(false); + // 恢复页面滚动 + document.body.style.overflow = ''; + }, []); + + const handleTouchEnd = useCallback(() => { + setIsDragging(false); + // 恢复页面滚动 + document.body.style.overflow = ''; }, []); useEffect(() => { if (isDragging) { document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseup', handleMouseUp); + document.addEventListener('touchmove', handleTouchMove, { passive: false }); + document.addEventListener('touchend', handleTouchEnd); + return () => { document.removeEventListener('mousemove', handleMouseMove); document.removeEventListener('mouseup', handleMouseUp); + document.removeEventListener('touchmove', handleTouchMove); + document.removeEventListener('touchend', handleTouchEnd); + // 清理时恢复滚动 + document.body.style.overflow = ''; }; } - }, [isDragging, handleMouseMove, handleMouseUp]); + }, [isDragging, handleMouseMove, handleMouseUp, handleTouchMove, handleTouchEnd]); // 重新渲染放大视图 useEffect(() => { @@ -205,15 +275,14 @@ const MagnifierTool: React.FC = ({ className="fixed bg-white dark:bg-gray-800 rounded-xl shadow-2xl border border-gray-200 dark:border-gray-600 z-50 select-none" style={{ left: magnifierPosition.x, - top: magnifierPosition.y, - maxWidth: '500px', - maxHeight: '500px' + top: magnifierPosition.y }} > {/* 标题栏 */}
@@ -249,7 +318,7 @@ const MagnifierTool: React.FC = ({ {/* 放大视图内容 */}
-
+