Compare commits

..

2 Commits

Author SHA1 Message Date
semantic-release-bot
be323bfbc5 chore(release): 4.63.2 [skip ci]
## [4.63.2](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.63.1...v4.63.2) (2025-10-21)

### Bug Fixes

* **flashcards:** correct pivot point to rotate around card center ([50fc3fd](50fc3fdf7f))
2025-10-21 16:19:23 +00:00
Thomas Hallock
50fc3fdf7f fix(flashcards): correct pivot point to rotate around card center
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>
2025-10-21 11:18:04 -05:00
3 changed files with 47 additions and 6 deletions

View File

@@ -1,3 +1,10 @@
## [4.63.2](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.63.1...v4.63.2) (2025-10-21)
### Bug Fixes
* **flashcards:** correct pivot point to rotate around card center ([50fc3fd](https://github.com/antialias/soroban-abacus-flashcards/commit/50fc3fdf7f2c9b7412f6d7d890f5e0d52cb86a9b))
## [4.63.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.63.0...v4.63.1) (2025-10-21)

View File

@@ -206,11 +206,45 @@ function DraggableCard({ card }: DraggableCardProps) {
)
}
// Update card position
setPosition({
x: dragStartRef.current.cardX + deltaX,
y: dragStartRef.current.cardY + deltaY,
})
// Update card position - keep the grab point "stuck" to the cursor
// As the card rotates, the grab point rotates with it, so we need to account for rotation
const rotationRad = (clampedRotation * Math.PI) / 180
const cosRot = Math.cos(rotationRad)
const sinRot = Math.sin(rotationRad)
// Rotate the grab offset by the current rotation angle
const rotatedGrabX = grabOffsetRef.current.x * cosRot - grabOffsetRef.current.y * sinRot
const rotatedGrabY = grabOffsetRef.current.x * sinRot + grabOffsetRef.current.y * cosRot
// Current cursor position
const cursorX = e.clientX
const cursorY = e.clientY
// Card center should be at: cursor position - rotated grab offset
// But we need to position the card element (top-left), not the center
// Get card dimensions to calculate offset from center to top-left
if (cardRef.current) {
const rect = cardRef.current.getBoundingClientRect()
const cardWidth = rect.width
const cardHeight = rect.height
// Card center position in screen space
const cardCenterX = cursorX - rotatedGrabX
const cardCenterY = cursorY - rotatedGrabY
// Convert center position to top-left position (what we store in position state)
// Note: position.x/y is used in translate(), which positions the element
setPosition({
x: cardCenterX - cardWidth / 2,
y: cardCenterY - cardHeight / 2,
})
} else {
// Fallback to simple delta if we don't have card dimensions yet
setPosition({
x: dragStartRef.current.cardX + deltaX,
y: dragStartRef.current.cardY + deltaY,
})
}
}
const handlePointerUp = (e: React.PointerEvent) => {

View File

@@ -1,6 +1,6 @@
{
"name": "soroban-monorepo",
"version": "4.63.1",
"version": "4.63.2",
"private": true,
"description": "Beautiful Soroban Flashcard Generator - Monorepo",
"workspaces": [