Add mouse wheel zoom to pattern preview
This commit is contained in:
@@ -700,6 +700,14 @@ export default function Home() {
|
||||
const cropDialogCanvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||
const patternStageRef = useRef<HTMLDivElement | null>(null);
|
||||
const zoomRef = useRef(zoom);
|
||||
const pendingWheelZoomRef = useRef<{
|
||||
stage: HTMLDivElement;
|
||||
cursorX: number;
|
||||
cursorY: number;
|
||||
canvasX: number;
|
||||
canvasY: number;
|
||||
} | null>(null);
|
||||
const cropDialogStageRef = useRef<HTMLDivElement | null>(null);
|
||||
const cropSelectionDragRef = useRef<{ pointerId: number; startX: number; startY: number } | null>(null);
|
||||
const patternDragRef = useRef<{
|
||||
@@ -959,6 +967,52 @@ export default function Home() {
|
||||
}
|
||||
}, [pixels, gridWidth, gridHeight, zoom, selectedCodes, onlySelected, showGrid, showCodes]);
|
||||
|
||||
useEffect(() => {
|
||||
zoomRef.current = zoom;
|
||||
const pending = pendingWheelZoomRef.current;
|
||||
if (!pending) return;
|
||||
pendingWheelZoomRef.current = null;
|
||||
const frame = window.requestAnimationFrame(() => {
|
||||
const { stage } = pending;
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
const stageRect = stage.getBoundingClientRect();
|
||||
const canvasRect = canvas.getBoundingClientRect();
|
||||
const anchorX = canvasRect.left + canvasRect.width * pending.canvasX;
|
||||
const anchorY = canvasRect.top + canvasRect.height * pending.canvasY;
|
||||
stage.scrollLeft += anchorX - (stageRect.left + pending.cursorX);
|
||||
stage.scrollTop += anchorY - (stageRect.top + pending.cursorY);
|
||||
});
|
||||
return () => window.cancelAnimationFrame(frame);
|
||||
}, [zoom]);
|
||||
|
||||
useEffect(() => {
|
||||
const stage = patternStageRef.current;
|
||||
if (!stage) return;
|
||||
const handleWheel = (event: WheelEvent) => {
|
||||
if (pixels.length === 0 || event.deltaY === 0) return;
|
||||
event.preventDefault();
|
||||
const currentZoom = zoomRef.current;
|
||||
const nextZoom = Math.max(3, Math.min(42, currentZoom + (event.deltaY < 0 ? 1 : -1)));
|
||||
if (nextZoom === currentZoom) return;
|
||||
const rect = stage.getBoundingClientRect();
|
||||
const canvasRect = canvasRef.current?.getBoundingClientRect();
|
||||
const cursorX = event.clientX - rect.left;
|
||||
const cursorY = event.clientY - rect.top;
|
||||
pendingWheelZoomRef.current = {
|
||||
stage,
|
||||
cursorX,
|
||||
cursorY,
|
||||
canvasX: canvasRect ? (event.clientX - canvasRect.left) / Math.max(1, canvasRect.width) : 0.5,
|
||||
canvasY: canvasRect ? (event.clientY - canvasRect.top) / Math.max(1, canvasRect.height) : 0.5,
|
||||
};
|
||||
zoomRef.current = nextZoom;
|
||||
setZoom(nextZoom);
|
||||
};
|
||||
stage.addEventListener("wheel", handleWheel, { passive: false });
|
||||
return () => stage.removeEventListener("wheel", handleWheel);
|
||||
}, [pixels.length]);
|
||||
|
||||
const handleUpload = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const file = event.target.files?.[0];
|
||||
if (!file) return;
|
||||
@@ -1274,6 +1328,7 @@ export default function Home() {
|
||||
<div
|
||||
className="canvas-stage"
|
||||
ref={patternStageRef}
|
||||
title="鼠标位于预览区时,可滚动滚轮缩放图纸"
|
||||
>
|
||||
<div className="canvas-wrap">
|
||||
<canvas
|
||||
|
||||
Reference in New Issue
Block a user