Fixed issue where cards appeared to pivot around the grab point in
viewport space instead of rotating around their own center.
The problem: When dragging, the card would stay "pinned" at the grab
point in screen space, making it rotate around that viewport location.
The solution: As the card rotates, calculate the rotated grab offset
and reposition the card so that:
1. The grab point stays under the cursor
2. The card rotates around its own center
3. The visual result feels like the card is "stuck" to your finger
Implementation:
- Convert rotation angle to radians
- Apply 2D rotation matrix to grab offset vector
- Calculate card center position: cursor - rotated grab offset
- Convert center position to top-left for CSS translate positioning
This creates the natural feeling of grabbing and spinning a physical
card by its edge.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The previous scale factor of 5000 made rotation changes too subtle to see.
Reduced to 500 (10x more sensitive) so card rotation is clearly visible
when dragging from off-center grab points.
Also added detailed rotation logging during drag to help debug:
- Shows current rotation angle
- Shows rotation influence being applied
- Shows cross product value from physics calculation
This will help test and tune the rotation feel.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements physics-based rotation that responds to where the user grabs
the card. When you grab a card off-center and drag it, it rotates
naturally based on the grab point and drag direction.
Features:
- Calculate grab offset from card center on pointer down
- Apply rotation using cross product of grab offset and drag direction
- Rotation clamped to ±45° to prevent excessive spinning
- Final rotation preserved when card is released
- Console logging for grab point coordinates and rotation changes
Physics details:
- Cross product (grabOffset.x * deltaY - grabOffset.y * deltaX) determines
rotation direction and magnitude
- Grabbing left side + dragging right = clockwise rotation
- Grabbing right side + dragging left = counter-clockwise rotation
- Scale factor of 5000 provides smooth, realistic rotation feel
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed logging issue where speed logs weren't showing during drag:
- Added separate lastLogTimeRef for logging throttle
- Logs now appear every ~200ms during drag (was never logging before)
- Velocity calculation unchanged, only logging throttle fixed
Now you can see speed values in console during drag to verify
shadow responsiveness.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
First physics enhancement - shadow changes based on how fast you drag:
- Tracks drag velocity (distance/time) during pointer move
- Shadow grows larger and darker with faster dragging
- Base: 8px offset, 24px blur, 0.3 opacity
- Fast: 32px offset, 64px blur, 0.6 opacity
- Smooth decay when released
Console logging included:
- [Shadow] logs on drag start/release
- Speed/distance/time logged during drag (throttled to ~100ms)
Test: Drag cards slowly vs fast and watch shadow change
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replaced complex react-spring + useDrag implementation with simple
PointerEvents-based drag and drop:
- Uses native onPointerDown/Move/Up events
- Tracks position with useState (no animation library)
- Cards stay exactly where dropped (no physics or snap-back)
- Simple scale-up effect while dragging
- Much more predictable and maintainable
Removed dependencies on:
- @react-spring/web
- @use-gesture/react
- Complex velocity tracking and decay physics
- Transform-origin calculations
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed conflicting top-level config that was interfering with decay
animations. Now using explicit config objects for each property:
- x, y: decay physics with velocity
- scale, rotation: wobbly spring animations
This should fix the issue where cards were snapping back to pickup
position instead of staying where dropped.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed the issue where cards were snapping back to pickup position:
- Use api.set() to immediately snap spring position to dropped location
- Then apply decay animation with momentum from that position
- Removed problematic 'from' property which doesn't work with decay
The bug was that react-spring's 'from' property is ignored when using
decay: true, causing the spring to animate from its current value rather
than the specified position. Using api.set() first ensures the spring
starts from the correct dropped position.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Made interactive flashcards a fun easter egg by:
- Changed overflow from 'hidden' to 'visible' to allow cards to be
thrown anywhere on the page, not just within container bounds
- Fixed position persistence: cards now stay exactly where dropped
instead of snapping back to pickup location
- Updated currentPositionRef immediately on drop before applying
momentum physics
Cards can now be dragged and tossed freely around the entire page
and will maintain their position after being dropped.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add physics-based flashcard component with drag, throw, and momentum:
- Random generation of 8-15 flashcards with abacus visualizations
- Smooth drag interactions with velocity-based rotation
- Decay physics for realistic throwing and sliding
- Transform-origin pivot point based on click position
- Position persistence so cards stay where thrown
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fix flashcards being positioned outside visible area by using the
container's actual dimensions instead of hardcoded pixel values.
The previous implementation used CONTAINER_WIDTH=800px and
CONTAINER_HEIGHT=500px to position cards, but the actual container
width is 100% which varies by screen size. This caused cards to be
positioned outside the visible area on smaller screens.
Changes:
- Add containerRef to get actual container dimensions
- Calculate card positions based on offsetWidth/offsetHeight
- Remove hardcoded dimension constants
- Ensure cards stay within visible bounds with proper margins
This makes the flashcard positioning responsive to any screen size.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fix interactive flashcards not rendering by properly converting
react-spring animated values to CSS transforms. The x, y, rotation,
and scale spring values now use the `to` helper to create proper
CSS transform strings.
Changes:
- Import `to` helper from @react-spring/web
- Convert spring values to CSS transform using translate/rotate/scale
- Set position to absolute with left:0, top:0 as transform origin
This fixes the issue where flashcards were invisible because the
spring values weren't being properly converted to CSS.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add a fun, interactive flashcard display to the homepage's flashcard
generator section. Users can drag and throw 8-15 randomly generated
flashcards around with realistic physics-based momentum.
Features:
- Drag and drop flashcards with mouse/touch
- Throw cards with velocity-based physics
- 8-15 randomly generated flashcards (100-999 range)
- Real AbacusReact components for each card
- Client-side rendering to avoid hydration errors
Technical implementation:
- Uses @use-gesture/react for drag gesture handling
- Uses @react-spring/web for smooth physics animations
- Cards generated client-side with useEffect to prevent SSR mismatch
- Each card maintains its own spring-based position and rotation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>