feat: implement realistic abacus drag mechanics

- Constrain bead dragging to vertical movement only (beads slide on rods)
- Add intelligent drag-to-toggle behavior based on direction and bead type
- Heaven beads: drag toward/away from reckoning bar toggles state
- Earth beads: drag toward bar activates, away deactivates
- Require significant drag distance to prevent accidental toggles
- Always snap back to proper position after drag release
- Mirrors real abacus physics and user expectations

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Thomas Hallock
2025-09-18 08:29:32 -05:00
parent 1303c930f2
commit 86cbbc8c18

View File

@@ -252,14 +252,38 @@ const Bead: React.FC<BeadProps> = ({
const [{ x: springX, y: springY }, api] = useSpring(() => ({ x, y }));
const bind = useDrag(
({ movement: [mx, my], down }) => {
({ movement: [mx, my], down, velocity: [vx, vy] }) => {
if (!draggable) return;
if (down) {
api.start({ x: x + mx, y: y + my, immediate: true });
onDrag?.({ x: mx, y: my });
// Constrain movement to vertical only (beads slide on rods)
const constrainedY = y + my;
api.start({ x, y: constrainedY, immediate: true });
onDrag?.({ x: 0, y: my }); // Report only vertical movement
} else {
api.start({ x, y, config: { tension: 400, friction: 30, mass: 0.8 } }); // Fast snap-back
// Determine snap behavior based on bead type and drag direction
const dragThreshold = size; // Minimum drag distance to trigger toggle
const wasSignificantDrag = Math.abs(my) > dragThreshold;
if (wasSignificantDrag) {
// Toggle bead state based on drag direction and current state
if (bead.type === 'heaven') {
// Heaven bead: drag toward/away from reckoning bar toggles state
const dragTowardBar = my > 0; // positive Y is toward bar
if ((dragTowardBar && !bead.active) || (!dragTowardBar && bead.active)) {
onClick?.(); // Toggle the bead
}
} else {
// Earth bead: drag toward bar activates, away deactivates
const dragTowardBar = my < 0; // negative Y is toward bar for earth beads
if ((dragTowardBar && !bead.active) || (!dragTowardBar && bead.active)) {
onClick?.(); // Toggle the bead
}
}
}
// Always snap back to proper position
api.start({ x, y, config: { tension: 400, friction: 30, mass: 0.8 } });
}
},
{ enabled: draggable }