优化放大镜工具的状态管理,增加放大镜切换时清除选择区域的逻辑,改进触摸和鼠标拖拽事件处理,确保用户交互流畅性和页面滚动控制,提升整体用户体验。
This commit is contained in:
@@ -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}
|
||||
|
||||
@@ -85,10 +85,14 @@ const FloatingColorPalette: React.FC<FloatingColorPaletteProps> = ({
|
||||
};
|
||||
|
||||
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<FloatingColorPaletteProps> = ({
|
||||
|
||||
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<FloatingColorPaletteProps> = ({
|
||||
document.removeEventListener('mouseup', handleEnd);
|
||||
document.removeEventListener('touchmove', handleTouchMove);
|
||||
document.removeEventListener('touchend', handleEnd);
|
||||
// 清理时恢复滚动
|
||||
document.body.style.overflow = '';
|
||||
};
|
||||
}
|
||||
}, [isDragging, dragOffset]);
|
||||
|
||||
@@ -68,14 +68,24 @@ const MagnifierSelectionOverlay: React.FC<MagnifierSelectionOverlayProps> = ({
|
||||
// 不再强制恢复滚动位置,让浏览器保持自然状态
|
||||
}, [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<MagnifierSelectionOverlayProps> = ({
|
||||
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<MagnifierSelectionOverlayProps> = ({
|
||||
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<MagnifierSelectionOverlayProps> = ({
|
||||
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,
|
||||
|
||||
@@ -32,8 +32,15 @@ const MagnifierTool: React.FC<MagnifierToolProps> = ({
|
||||
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<boolean>(false);
|
||||
const [dragOffset, setDragOffset] = useState<{ x: number; y: number }>({ x: 0, y: 0 });
|
||||
|
||||
const magnifierRef = useRef<HTMLDivElement>(null);
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
@@ -62,14 +69,9 @@ const MagnifierTool: React.FC<MagnifierToolProps> = ({
|
||||
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<MagnifierToolProps> = ({
|
||||
}
|
||||
}, [selectionArea, mappedPixelData, selectedColor, onPixelEdit]);
|
||||
|
||||
// 处理拖拽移动
|
||||
// 处理拖拽移动 - 鼠标事件
|
||||
const handleTitleBarMouseDown = useCallback((event: React.MouseEvent) => {
|
||||
// 只有点击在标题栏区域且不是按钮时才开始拖拽
|
||||
const target = event.target as HTMLElement;
|
||||
@@ -149,33 +151,101 @@ const MagnifierTool: React.FC<MagnifierToolProps> = ({
|
||||
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<MagnifierToolProps> = ({
|
||||
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
|
||||
}}
|
||||
>
|
||||
{/* 标题栏 */}
|
||||
<div
|
||||
className="flex items-center justify-between p-3 bg-gradient-to-r from-green-500 to-teal-500 text-white rounded-t-xl cursor-move"
|
||||
onMouseDown={handleTitleBarMouseDown}
|
||||
onTouchStart={handleTitleBarTouchStart}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
@@ -249,7 +318,7 @@ const MagnifierTool: React.FC<MagnifierToolProps> = ({
|
||||
|
||||
{/* 放大视图内容 */}
|
||||
<div className="p-3">
|
||||
<div className="border border-gray-300 dark:border-gray-600 rounded-lg overflow-hidden">
|
||||
<div className="border border-gray-300 dark:border-gray-600 rounded-lg overflow-auto max-h-96">
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
onClick={handleMagnifiedClick}
|
||||
|
||||
Reference in New Issue
Block a user