Implements smooth drag animation
Implements a smooth drag animation for a more fluid user experience. Instead of directly snapping to the mouse position, it interpolates the position over time using `requestAnimationFrame`. This provides a visually smoother movement during drag operations.
This commit is contained in:
parent
3145752591
commit
a8b9767524
1 changed files with 74 additions and 17 deletions
|
|
@ -64,6 +64,11 @@ export class DragDropManager {
|
||||||
private snapIntervalMinutes = 15; // Default 15 minutes
|
private snapIntervalMinutes = 15; // Default 15 minutes
|
||||||
private hourHeightPx: number; // Will be set from config
|
private hourHeightPx: number; // Will be set from config
|
||||||
|
|
||||||
|
// Smooth drag animation
|
||||||
|
private dragAnimationId: number | null = null;
|
||||||
|
private targetY = 0;
|
||||||
|
private currentY = 0;
|
||||||
|
private targetColumn: ColumnBounds | null = null;
|
||||||
|
|
||||||
private get snapDistancePx(): number {
|
private get snapDistancePx(): number {
|
||||||
return (this.snapIntervalMinutes / 60) * this.hourHeightPx;
|
return (this.snapIntervalMinutes / 60) * this.hourHeightPx;
|
||||||
|
|
@ -257,25 +262,18 @@ export class DragDropManager {
|
||||||
// Continue with normal drag behavior only if drag has started
|
// Continue with normal drag behavior only if drag has started
|
||||||
if (this.isDragStarted && this.draggedElement && this.draggedClone) {
|
if (this.isDragStarted && this.draggedElement && this.draggedClone) {
|
||||||
if (!this.draggedElement.hasAttribute("data-allday")) {
|
if (!this.draggedElement.hasAttribute("data-allday")) {
|
||||||
const deltaY = Math.abs(currentPosition.y - this.lastLoggedPosition.y);
|
// Calculate raw target position from mouse (no snapping yet)
|
||||||
|
|
||||||
// Check for snap interval vertical movement (normal drag behavior)
|
|
||||||
if (deltaY >= this.snapDistancePx) {
|
|
||||||
this.lastLoggedPosition = currentPosition;
|
|
||||||
|
|
||||||
// Consolidated position calculations with snapping for normal drag
|
|
||||||
const positionData = this.calculateDragPosition(currentPosition);
|
const positionData = this.calculateDragPosition(currentPosition);
|
||||||
|
|
||||||
// Emit drag move event with snapped position (normal behavior)
|
if (positionData.column) {
|
||||||
const dragMovePayload: DragMoveEventPayload = {
|
this.targetY = positionData.snappedY; // Store raw Y as target
|
||||||
draggedElement: this.draggedElement,
|
this.targetColumn = positionData.column;
|
||||||
draggedClone: this.draggedClone,
|
|
||||||
mousePosition: currentPosition,
|
// Start animation loop if not already running
|
||||||
snappedY: positionData.snappedY,
|
if (this.dragAnimationId === null) {
|
||||||
columnBounds: positionData.column,
|
this.currentY = parseFloat(this.draggedClone.style.top) || 0;
|
||||||
mouseOffset: this.mouseOffset
|
this.animateDrag();
|
||||||
};
|
}
|
||||||
this.eventBus.emit('drag:move', dragMovePayload);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for auto-scroll
|
// Check for auto-scroll
|
||||||
|
|
@ -308,6 +306,7 @@ export class DragDropManager {
|
||||||
*/
|
*/
|
||||||
private handleMouseUp(event: MouseEvent): void {
|
private handleMouseUp(event: MouseEvent): void {
|
||||||
this.stopAutoScroll();
|
this.stopAutoScroll();
|
||||||
|
this.stopDragAnimation();
|
||||||
|
|
||||||
if (this.draggedElement) {
|
if (this.draggedElement) {
|
||||||
|
|
||||||
|
|
@ -391,6 +390,7 @@ export class DragDropManager {
|
||||||
// 4. Clean up state
|
// 4. Clean up state
|
||||||
this.cleanupDragState();
|
this.cleanupDragState();
|
||||||
this.stopAutoScroll();
|
this.stopAutoScroll();
|
||||||
|
this.stopDragAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -420,6 +420,53 @@ export class DragDropManager {
|
||||||
return Math.max(0, snappedY);
|
return Math.max(0, snappedY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Smooth drag animation using requestAnimationFrame
|
||||||
|
*/
|
||||||
|
private animateDrag(): void {
|
||||||
|
if (!this.isDragStarted || !this.draggedClone || !this.targetColumn) {
|
||||||
|
this.dragAnimationId = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smooth interpolation towards target
|
||||||
|
const diff = this.targetY - this.currentY;
|
||||||
|
const step = diff * 0.3; // 30% of distance per frame
|
||||||
|
|
||||||
|
// Update if difference is significant
|
||||||
|
if (Math.abs(diff) > 0.5) {
|
||||||
|
this.currentY += step;
|
||||||
|
|
||||||
|
// Emit drag move event with interpolated position
|
||||||
|
const dragMovePayload: DragMoveEventPayload = {
|
||||||
|
draggedElement: this.draggedElement!,
|
||||||
|
draggedClone: this.draggedClone,
|
||||||
|
mousePosition: this.lastMousePosition,
|
||||||
|
snappedY: this.currentY,
|
||||||
|
columnBounds: this.targetColumn,
|
||||||
|
mouseOffset: this.mouseOffset
|
||||||
|
};
|
||||||
|
this.eventBus.emit('drag:move', dragMovePayload);
|
||||||
|
|
||||||
|
this.dragAnimationId = requestAnimationFrame(() => this.animateDrag());
|
||||||
|
} else {
|
||||||
|
// Close enough - snap to target
|
||||||
|
this.currentY = this.targetY;
|
||||||
|
|
||||||
|
const dragMovePayload: DragMoveEventPayload = {
|
||||||
|
draggedElement: this.draggedElement!,
|
||||||
|
draggedClone: this.draggedClone,
|
||||||
|
mousePosition: this.lastMousePosition,
|
||||||
|
snappedY: this.currentY,
|
||||||
|
columnBounds: this.targetColumn,
|
||||||
|
mouseOffset: this.mouseOffset
|
||||||
|
};
|
||||||
|
this.eventBus.emit('drag:move', dragMovePayload);
|
||||||
|
|
||||||
|
this.dragAnimationId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optimized auto-scroll check with cached container
|
* Optimized auto-scroll check with cached container
|
||||||
|
|
@ -497,6 +544,16 @@ export class DragDropManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop drag animation
|
||||||
|
*/
|
||||||
|
private stopDragAnimation(): void {
|
||||||
|
if (this.dragAnimationId !== null) {
|
||||||
|
cancelAnimationFrame(this.dragAnimationId);
|
||||||
|
this.dragAnimationId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clean up drag state
|
* Clean up drag state
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue