Fix zoom race condition and adjust zoom limits

- Fix race condition by using functional state updates in all zoom handlers
- Prevents negative or invalid zoom values from rapid scroll events
- Change zoom range from 10%-1000% to 10%-200% for more reasonable limits
- All zoom operations (wheel, buttons) now safely constrained
- Ensures consistent behavior even with fast mouse wheel scrolling

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Jan-Henrik Bruhn 2025-12-10 14:32:27 +01:00
parent 6534684967
commit 39d4df6a74

View file

@ -91,76 +91,80 @@ export function PatternCanvas({ pesData, sewingProgress, machineInfo, initialPat
const stage = e.target.getStage();
if (!stage) return;
const oldScale = stage.scaleX();
const pointer = stage.getPointerPosition();
if (!pointer) return;
const scaleBy = 1.1;
const direction = e.evt.deltaY > 0 ? -1 : 1;
let newScale = direction > 0 ? oldScale * scaleBy : oldScale / scaleBy;
// Apply constraints
newScale = Math.max(0.1, Math.min(10, newScale));
setStageScale((oldScale) => {
const newScale = Math.max(0.1, Math.min(direction > 0 ? oldScale * scaleBy : oldScale / scaleBy, 2));
// Zoom towards pointer
const mousePointTo = {
x: (pointer.x - stage.x()) / oldScale,
y: (pointer.y - stage.y()) / oldScale,
};
// Zoom towards pointer
setStagePos((prevPos) => {
const mousePointTo = {
x: (pointer.x - prevPos.x) / oldScale,
y: (pointer.y - prevPos.y) / oldScale,
};
const newPos = {
x: pointer.x - mousePointTo.x * newScale,
y: pointer.y - mousePointTo.y * newScale,
};
return {
x: pointer.x - mousePointTo.x * newScale,
y: pointer.y - mousePointTo.y * newScale,
};
});
setStageScale(newScale);
setStagePos(newPos);
return newScale;
});
}, []);
// Zoom control handlers
const handleZoomIn = useCallback(() => {
const oldScale = stageScale;
const newScale = Math.min(oldScale * 1.2, 10);
setStageScale((oldScale) => {
const newScale = Math.max(0.1, Math.min(oldScale * 1.2, 2));
// Zoom towards center of viewport
const centerX = containerSize.width / 2;
const centerY = containerSize.height / 2;
// Zoom towards center of viewport
setStagePos((prevPos) => {
const centerX = containerSize.width / 2;
const centerY = containerSize.height / 2;
const mousePointTo = {
x: (centerX - stagePos.x) / oldScale,
y: (centerY - stagePos.y) / oldScale,
};
const mousePointTo = {
x: (centerX - prevPos.x) / oldScale,
y: (centerY - prevPos.y) / oldScale,
};
const newPos = {
x: centerX - mousePointTo.x * newScale,
y: centerY - mousePointTo.y * newScale,
};
return {
x: centerX - mousePointTo.x * newScale,
y: centerY - mousePointTo.y * newScale,
};
});
setStageScale(newScale);
setStagePos(newPos);
}, [stageScale, stagePos, containerSize]);
return newScale;
});
}, [containerSize]);
const handleZoomOut = useCallback(() => {
const oldScale = stageScale;
const newScale = Math.max(oldScale / 1.2, 0.1);
setStageScale((oldScale) => {
const newScale = Math.max(0.1, Math.min(oldScale / 1.2, 2));
// Zoom towards center of viewport
const centerX = containerSize.width / 2;
const centerY = containerSize.height / 2;
// Zoom towards center of viewport
setStagePos((prevPos) => {
const centerX = containerSize.width / 2;
const centerY = containerSize.height / 2;
const mousePointTo = {
x: (centerX - stagePos.x) / oldScale,
y: (centerY - stagePos.y) / oldScale,
};
const mousePointTo = {
x: (centerX - prevPos.x) / oldScale,
y: (centerY - prevPos.y) / oldScale,
};
const newPos = {
x: centerX - mousePointTo.x * newScale,
y: centerY - mousePointTo.y * newScale,
};
return {
x: centerX - mousePointTo.x * newScale,
y: centerY - mousePointTo.y * newScale,
};
});
setStageScale(newScale);
setStagePos(newPos);
}, [stageScale, stagePos, containerSize]);
return newScale;
});
}, [containerSize]);
const handleZoomReset = useCallback(() => {
const initialScale = initialScaleRef.current;