diff --git a/src/components/PatternCanvas/KonvaComponents.tsx b/src/components/PatternCanvas/KonvaComponents.tsx
index b640ce9..2d4c27b 100644
--- a/src/components/PatternCanvas/KonvaComponents.tsx
+++ b/src/components/PatternCanvas/KonvaComponents.tsx
@@ -53,7 +53,6 @@ export const Grid = memo(({ gridSize, bounds, machineInfo }: GridProps) => {
points={points}
stroke={gridColor}
strokeWidth={1}
- listening={false}
/>
))}
{lines.horizontalLines.map((points, i) => (
@@ -62,7 +61,6 @@ export const Grid = memo(({ gridSize, bounds, machineInfo }: GridProps) => {
points={points}
stroke={gridColor}
strokeWidth={1}
- listening={false}
/>
))}
@@ -76,18 +74,8 @@ export const Origin = memo(() => {
return (
-
-
+
+
);
});
@@ -114,7 +102,6 @@ export const Hoop = memo(({ machineInfo }: HoopProps) => {
stroke={hoopColor}
strokeWidth={3}
dash={[10, 5]}
- listening={false}
/>
{
fontFamily="sans-serif"
fontStyle="bold"
fill={hoopColor}
- listening={false}
/>
);
diff --git a/src/hooks/ui/useCanvasViewport.ts b/src/hooks/ui/useCanvasViewport.ts
index d3677d8..88e2048 100644
--- a/src/hooks/ui/useCanvasViewport.ts
+++ b/src/hooks/ui/useCanvasViewport.ts
@@ -2,7 +2,7 @@
* useCanvasViewport Hook
*
* Manages canvas viewport state including zoom, pan, and container size
- * Handles wheel zoom and button zoom operations
+ * Handles wheel zoom, button zoom operations, and stage drag cursor updates
*/
import {
@@ -93,43 +93,45 @@ export function useCanvasViewport({
setStagePos({ x: containerSize.width / 2, y: containerSize.height / 2 });
}
- // Wheel zoom handler with RAF throttling
+ // Wheel zoom handler with RAF throttling and delta accumulation
const wheelThrottleRef = useRef(null);
- const wheelEventRef = useRef | null>(null);
+ const accumulatedDeltaRef = useRef(0);
+ const lastPointerRef = useRef<{ x: number; y: number } | null>(null);
+ const lastStageRef = useRef(null);
const handleWheel = useCallback((e: Konva.KonvaEventObject) => {
e.evt.preventDefault();
- // Store the latest event
- wheelEventRef.current = e;
+ const stage = e.target.getStage();
+ if (!stage) return;
- // Cancel pending throttle if it exists
+ const pointer = stage.getPointerPosition();
+ if (!pointer) return;
+
+ // Accumulate deltaY from all events during throttle period
+ accumulatedDeltaRef.current += e.evt.deltaY;
+ lastPointerRef.current = pointer;
+ lastStageRef.current = stage;
+
+ // Skip if throttle already in progress
if (wheelThrottleRef.current !== null) {
- return; // Throttle in progress, skip this event
+ return;
}
// Schedule update on next animation frame (~16ms)
wheelThrottleRef.current = requestAnimationFrame(() => {
- const throttledEvent = wheelEventRef.current;
- if (!throttledEvent) {
- wheelThrottleRef.current = null;
- return;
- }
+ const accumulatedDelta = accumulatedDeltaRef.current;
+ const pointer = lastPointerRef.current;
+ const stage = lastStageRef.current;
- const stage = throttledEvent.target.getStage();
- if (!stage) {
- wheelThrottleRef.current = null;
- return;
- }
-
- const pointer = stage.getPointerPosition();
- if (!pointer) {
+ if (!pointer || !stage || accumulatedDelta === 0) {
wheelThrottleRef.current = null;
+ accumulatedDeltaRef.current = 0;
return;
}
const scaleBy = 1.1;
- const direction = throttledEvent.evt.deltaY > 0 ? -1 : 1;
+ const direction = accumulatedDelta > 0 ? -1 : 1;
setStageScale((oldScale) => {
const newScale = Math.max(
@@ -145,8 +147,9 @@ export function useCanvasViewport({
return newScale;
});
+ // Reset accumulator and throttle
wheelThrottleRef.current = null;
- wheelEventRef.current = null;
+ accumulatedDeltaRef.current = 0;
});
}, []);
@@ -199,19 +202,12 @@ export function useCanvasViewport({
setStagePos({ x: containerSize.width / 2, y: containerSize.height / 2 });
}, [initialScale, containerSize]);
- // Stage drag handlers with throttled cursor updates
- const lastCursorUpdateRef = useRef(0);
-
+ // Stage drag handlers - cursor updates immediately for better UX
const handleStageDragStart = useCallback(
(e: Konva.KonvaEventObject) => {
- const now = Date.now();
- // Throttle cursor updates to ~60fps (16ms)
- if (now - lastCursorUpdateRef.current > 16) {
- const stage = e.target.getStage();
- if (stage) {
- stage.container().style.cursor = "grabbing";
- }
- lastCursorUpdateRef.current = now;
+ const stage = e.target.getStage();
+ if (stage) {
+ stage.container().style.cursor = "grabbing";
}
},
[],
@@ -223,7 +219,6 @@ export function useCanvasViewport({
if (stage) {
stage.container().style.cursor = "grab";
}
- lastCursorUpdateRef.current = 0;
},
[],
);