Skip to content

GraphComponent.onDrag callback provides only incremental diff, missing absolute diff from drag start #193

@draedful

Description

@draedful

The onDragUpdate callback in onDrag method provides diffX/diffY values that represent the incremental difference between the previous and current mouse position, not the absolute difference from the drag start position.

This is unexpected behavior and makes it difficult to implement common drag patterns where you need to know the total displacement from the starting point.

Current Behavior

onDragUpdate?: (
  diff: {
    prevCoords: [number, number];
    currentCoords: [number, number];
    diffX: number;  // difference between prevCoords and currentCoords
    diffY: number;  // resets each frame
  },
  event: MouseEvent
) => void;

The diffX/diffY values are calculated as:

const diffX = (startDragCoords[0] - currentCoords[0]) | 0;
const diffY = (startDragCoords[1] - currentCoords[1]) | 0;
// ...

startDragCoords = currentCoords;  // resets after each update

This means:

  • diffX/diffY represent only the movement since the last frame
  • Cumulative tracking requires manual accumulation in user code
  • Integer truncation (| 0) combined with per-frame reset causes precision loss
    `

Expected Behavior

Provide absolute diff from drag start, or add additional fields for both use cases:

onDragUpdate?: (
  diff: {
    startCoords: [number, number];    // initial position when drag started
    prevCoords: [number, number];     // position on previous frame
    currentCoords: [number, number];  // current position
    
    // Absolute diff from start (most common use case)
    diffX: number;      // currentCoords.x - startCoords.x
    diffY: number;      // currentCoords.y - startCoords.y
    
    // Incremental diff from previous frame (for specific use cases)
    deltaDiffX: number; // currentCoords.x - prevCoords.x
    deltaDiffY: number; // currentCoords.y - prevCoords.y
  },
  event: MouseEvent
) => void;

Use Case Example

When implementing a draggable component, the typical pattern is:

// What users expect to write:
onDragUpdate: (diff) => {
  element.x = initialX + diff.diffX;  // absolute position from start
  element.y = initialY + diff.diffY;
}

// What users currently have to write:
let accumulatedX = 0;
let accumulatedY = 0;

onDragUpdate: (diff) => {
  accumulatedX += diff.diffX;  // manual accumulation
  accumulatedY += diff.diffY;
  element.x = initialX + accumulatedX;
  element.y = initialY + accumulatedY;
}

Suggested Solution

Option 1: Change diffX/diffY to absolute values (breaking change):

// Keep startDragCoords constant, don't reset after each update
const diffX = startDragCoords[0] - currentCoords[0];
const diffY = startDragCoords[1] - currentCoords[1];
// Remove: startDragCoords = currentCoords;

Option 2: Add new fields while keeping backward compatibility:

diff: {
  prevCoords: [number, number];
  currentCoords: [number, number];
  startCoords: [number, number];      // NEW: initial drag position
  
  diffX: number;       // keep current behavior for backward compatibility
  diffY: number;
  
  totalDiffX: number;  // NEW: absolute diff from start
  totalDiffY: number;  // NEW: absolute diff from start
}

Additionally needs to remove integer truncation and document the incremental behavior:

const diffX = startDragCoords[0] - currentCoords[0];  // no truncation
const diffY = startDragCoords[1] - currentCoords[1];

Additional Context

  • The current incremental approach with | 0 truncation causes visible drag lag, especially with slow mouse movements
  • Most drag implementations expect absolute diff from start position
  • The prevCoords field suggests incremental tracking is intentional, but having both options would improve DX

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions