fix: ensure card detaches from leader and clears followers when starting a drag, and centralize stack property recalculations.

This commit is contained in:
2025-11-28 01:19:28 +01:00
parent 635747a1ee
commit 7c03eecd30
2 changed files with 47 additions and 10 deletions
+5 -5
View File
@@ -4,8 +4,11 @@ export const handleDragStart = (store: LayoutStore, selection: string[], leading
const leadingCard = store.items.get(leadingId);
if (!leadingCard) return;
// 1. Snap all followers to leader's center
// 2. Attach them as followers to the leader's physics
// 1. Begin drag on the leader (clears old followers)
leadingCard.physics.beginDrag(offset, pointerId);
// 2. Snap all followers to leader's center
// 3. Attach them as followers to the leader's physics
selection.forEach(id => {
if (id === leadingId) return;
const card = store.items.get(id);
@@ -28,9 +31,6 @@ export const handleDragStart = (store: LayoutStore, selection: string[], leading
leadingCard.physics.addFollower(card);
}
});
// 3. Begin drag ONLY on the leader
leadingCard.physics.beginDrag(offset, pointerId);
};
export const attachToDragGroup = (store: LayoutStore, selection: string[], leadingId: string, pointerId: number) => {
+42 -5
View File
@@ -46,6 +46,14 @@ export class CardPhysics {
}
beginDrag(offset: { x: number, y: number }, pointerId: number) {
// If I am a follower of someone else, detach first!
if (this.leader) {
this.leader.removeFollower(this.card);
}
// Ensure I don't have stale followers from a previous session
this.stopPhysicsLoop();
this._isDragging = true;
this.dragPointerId = pointerId;
@@ -113,6 +121,7 @@ export class CardPhysics {
private maxLimit: number = 5;
private followers: { card: LayoutCard, offsetRotation: number }[] = [];
public leader: CardPhysics | null = null;
addFollower(card: LayoutCard) {
// Calculate relative rotation
@@ -120,25 +129,53 @@ export class CardPhysics {
const offset = this.normalizeAngle(card.rotation - this.card.rotation);
this.followers.push({ card, offsetRotation: offset });
// Set back-reference
card.physics.leader = this;
// Add follower mass to leader
this.mass += card.physics.mass;
this.updateMassScale();
// Constrain leader limits to ensure follower stays within [-5, 5]
// -5 <= leader + offset <= 5
// -5 - offset <= leader <= 5 - offset
this.minLimit = Math.max(this.minLimit, -this.ROTATION_LIMIT - offset);
this.maxLimit = Math.min(this.maxLimit, this.ROTATION_LIMIT - offset);
}
removeFollower(card: LayoutCard) {
const index = this.followers.findIndex(f => f.card === card);
if (index !== -1) {
const follower = this.followers[index];
follower.card.physics.leader = null;
this.followers.splice(index, 1);
// Recalculate mass and limits
this.recalculateStackProperties();
}
}
clearFollowers() {
// Clear back-references
this.followers.forEach(f => {
f.card.physics.leader = null;
});
this.followers = [];
// Reset mass to base mass
this.recalculateStackProperties();
}
private recalculateStackProperties() {
this.mass = this.baseMass;
this.updateMassScale();
// Reset limits
this.minLimit = -this.ROTATION_LIMIT;
this.maxLimit = this.ROTATION_LIMIT;
for (const f of this.followers) {
this.mass += f.card.physics.mass;
// Re-apply limits
const offset = f.offsetRotation;
this.minLimit = Math.max(this.minLimit, -this.ROTATION_LIMIT - offset);
this.maxLimit = Math.min(this.maxLimit, this.ROTATION_LIMIT - offset);
}
this.updateMassScale();
}
private updatePhysics(dt: number) {