mirror of
https://github.com/jhbruhn/respira.git
synced 2026-01-27 02:13:41 +00:00
fix: Address Copilot review feedback for performance optimizations
Fix issues identified in Copilot review:
1. Remove throttling from stage drag cursor updates - cursor now updates immediately on drag start for better UX
2. Accumulate wheel deltaY values during throttle period instead of only processing last event - prevents jerky zoom behavior
3. Remove redundant listening={false} props from child elements (inherited from parent Group)
4. Update file documentation to reflect stage drag cursor update functionality
These changes improve both performance and user experience while maintaining code clarity.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
93ebc8398c
commit
9d30eae901
2 changed files with 31 additions and 50 deletions
|
|
@ -53,7 +53,6 @@ export const Grid = memo(({ gridSize, bounds, machineInfo }: GridProps) => {
|
||||||
points={points}
|
points={points}
|
||||||
stroke={gridColor}
|
stroke={gridColor}
|
||||||
strokeWidth={1}
|
strokeWidth={1}
|
||||||
listening={false}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{lines.horizontalLines.map((points, i) => (
|
{lines.horizontalLines.map((points, i) => (
|
||||||
|
|
@ -62,7 +61,6 @@ export const Grid = memo(({ gridSize, bounds, machineInfo }: GridProps) => {
|
||||||
points={points}
|
points={points}
|
||||||
stroke={gridColor}
|
stroke={gridColor}
|
||||||
strokeWidth={1}
|
strokeWidth={1}
|
||||||
listening={false}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Group>
|
</Group>
|
||||||
|
|
@ -76,18 +74,8 @@ export const Origin = memo(() => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group name="origin" listening={false}>
|
<Group name="origin" listening={false}>
|
||||||
<Line
|
<Line points={[-10, 0, 10, 0]} stroke={originColor} strokeWidth={2} />
|
||||||
points={[-10, 0, 10, 0]}
|
<Line points={[0, -10, 0, 10]} stroke={originColor} strokeWidth={2} />
|
||||||
stroke={originColor}
|
|
||||||
strokeWidth={2}
|
|
||||||
listening={false}
|
|
||||||
/>
|
|
||||||
<Line
|
|
||||||
points={[0, -10, 0, 10]}
|
|
||||||
stroke={originColor}
|
|
||||||
strokeWidth={2}
|
|
||||||
listening={false}
|
|
||||||
/>
|
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
@ -114,7 +102,6 @@ export const Hoop = memo(({ machineInfo }: HoopProps) => {
|
||||||
stroke={hoopColor}
|
stroke={hoopColor}
|
||||||
strokeWidth={3}
|
strokeWidth={3}
|
||||||
dash={[10, 5]}
|
dash={[10, 5]}
|
||||||
listening={false}
|
|
||||||
/>
|
/>
|
||||||
<Text
|
<Text
|
||||||
x={hoopLeft + 10}
|
x={hoopLeft + 10}
|
||||||
|
|
@ -124,7 +111,6 @@ export const Hoop = memo(({ machineInfo }: HoopProps) => {
|
||||||
fontFamily="sans-serif"
|
fontFamily="sans-serif"
|
||||||
fontStyle="bold"
|
fontStyle="bold"
|
||||||
fill={hoopColor}
|
fill={hoopColor}
|
||||||
listening={false}
|
|
||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
* useCanvasViewport Hook
|
* useCanvasViewport Hook
|
||||||
*
|
*
|
||||||
* Manages canvas viewport state including zoom, pan, and container size
|
* 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 {
|
import {
|
||||||
|
|
@ -93,43 +93,45 @@ export function useCanvasViewport({
|
||||||
setStagePos({ x: containerSize.width / 2, y: containerSize.height / 2 });
|
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<number | null>(null);
|
const wheelThrottleRef = useRef<number | null>(null);
|
||||||
const wheelEventRef = useRef<Konva.KonvaEventObject<WheelEvent> | null>(null);
|
const accumulatedDeltaRef = useRef<number>(0);
|
||||||
|
const lastPointerRef = useRef<{ x: number; y: number } | null>(null);
|
||||||
|
const lastStageRef = useRef<Konva.Stage | null>(null);
|
||||||
|
|
||||||
const handleWheel = useCallback((e: Konva.KonvaEventObject<WheelEvent>) => {
|
const handleWheel = useCallback((e: Konva.KonvaEventObject<WheelEvent>) => {
|
||||||
e.evt.preventDefault();
|
e.evt.preventDefault();
|
||||||
|
|
||||||
// Store the latest event
|
const stage = e.target.getStage();
|
||||||
wheelEventRef.current = e;
|
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) {
|
if (wheelThrottleRef.current !== null) {
|
||||||
return; // Throttle in progress, skip this event
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Schedule update on next animation frame (~16ms)
|
// Schedule update on next animation frame (~16ms)
|
||||||
wheelThrottleRef.current = requestAnimationFrame(() => {
|
wheelThrottleRef.current = requestAnimationFrame(() => {
|
||||||
const throttledEvent = wheelEventRef.current;
|
const accumulatedDelta = accumulatedDeltaRef.current;
|
||||||
if (!throttledEvent) {
|
const pointer = lastPointerRef.current;
|
||||||
wheelThrottleRef.current = null;
|
const stage = lastStageRef.current;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const stage = throttledEvent.target.getStage();
|
if (!pointer || !stage || accumulatedDelta === 0) {
|
||||||
if (!stage) {
|
|
||||||
wheelThrottleRef.current = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pointer = stage.getPointerPosition();
|
|
||||||
if (!pointer) {
|
|
||||||
wheelThrottleRef.current = null;
|
wheelThrottleRef.current = null;
|
||||||
|
accumulatedDeltaRef.current = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const scaleBy = 1.1;
|
const scaleBy = 1.1;
|
||||||
const direction = throttledEvent.evt.deltaY > 0 ? -1 : 1;
|
const direction = accumulatedDelta > 0 ? -1 : 1;
|
||||||
|
|
||||||
setStageScale((oldScale) => {
|
setStageScale((oldScale) => {
|
||||||
const newScale = Math.max(
|
const newScale = Math.max(
|
||||||
|
|
@ -145,8 +147,9 @@ export function useCanvasViewport({
|
||||||
return newScale;
|
return newScale;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Reset accumulator and throttle
|
||||||
wheelThrottleRef.current = null;
|
wheelThrottleRef.current = null;
|
||||||
wheelEventRef.current = null;
|
accumulatedDeltaRef.current = 0;
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
@ -199,20 +202,13 @@ export function useCanvasViewport({
|
||||||
setStagePos({ x: containerSize.width / 2, y: containerSize.height / 2 });
|
setStagePos({ x: containerSize.width / 2, y: containerSize.height / 2 });
|
||||||
}, [initialScale, containerSize]);
|
}, [initialScale, containerSize]);
|
||||||
|
|
||||||
// Stage drag handlers with throttled cursor updates
|
// Stage drag handlers - cursor updates immediately for better UX
|
||||||
const lastCursorUpdateRef = useRef<number>(0);
|
|
||||||
|
|
||||||
const handleStageDragStart = useCallback(
|
const handleStageDragStart = useCallback(
|
||||||
(e: Konva.KonvaEventObject<DragEvent>) => {
|
(e: Konva.KonvaEventObject<DragEvent>) => {
|
||||||
const now = Date.now();
|
|
||||||
// Throttle cursor updates to ~60fps (16ms)
|
|
||||||
if (now - lastCursorUpdateRef.current > 16) {
|
|
||||||
const stage = e.target.getStage();
|
const stage = e.target.getStage();
|
||||||
if (stage) {
|
if (stage) {
|
||||||
stage.container().style.cursor = "grabbing";
|
stage.container().style.cursor = "grabbing";
|
||||||
}
|
}
|
||||||
lastCursorUpdateRef.current = now;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
@ -223,7 +219,6 @@ export function useCanvasViewport({
|
||||||
if (stage) {
|
if (stage) {
|
||||||
stage.container().style.cursor = "grab";
|
stage.container().style.cursor = "grab";
|
||||||
}
|
}
|
||||||
lastCursorUpdateRef.current = 0;
|
|
||||||
},
|
},
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue