Skip to content

Commit 696f4b7

Browse files
authored
feat(GraphComponent)!: provide absolute diff from drag start in onDrag callback (#194)
BREAKING CHANGE: `diffX`/`diffY` in `onDragUpdate` callback now represent absolute displacement from drag start position instead of incremental frame-to-frame values. Changes: - `diffX`/`diffY` now calculated as `currentCoords - startCoords` - Added `startCoords` - initial position when drag started - Added `deltaX`/`deltaY` - incremental change since previous frame - Removed integer truncation (`| 0`) for better precision with slow movements
1 parent 0de6d11 commit 696f4b7

File tree

4 files changed

+62
-25
lines changed

4 files changed

+62
-25
lines changed

docs/components/canvas-graph-component.md

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,13 @@ this.onDrag({
139139
this.setState({ dragging: true });
140140
this.context.graph.emit('element-drag-start', { element: this });
141141
},
142-
onDragUpdate: ({ diffX, diffY }, event) => {
143-
this.updatePosition(this.state.x - diffX, this.state.y - diffY);
142+
onDragUpdate: ({ startCoords, diffX, diffY, deltaX, deltaY }, event) => {
143+
// diffX/diffY - absolute displacement from drag start position
144+
// deltaX/deltaY - incremental change since previous frame
145+
// Use diffX/diffY for absolute positioning from initial position
146+
this.updatePosition(this.initialX + diffX, this.initialY + diffY);
147+
// Or use deltaX/deltaY for incremental updates
148+
// this.updatePosition(this.state.x + deltaX, this.state.y + deltaY);
144149
},
145150
onDrop: () => {
146151
this.setState({ dragging: false });
@@ -163,6 +168,25 @@ this.onDrag({
163168
- `mouseenter`, `mouseleave` - Mouse pointer entering or leaving the component
164169
- Specialized `onDrag` system with precise coordinate handling
165170

171+
**The `onDrag` callback structure:**
172+
173+
```typescript
174+
onDragUpdate: (diff: {
175+
startCoords: [number, number]; // Initial position when drag started
176+
prevCoords: [number, number]; // Position on previous frame
177+
currentCoords: [number, number]; // Current mouse position
178+
179+
diffX: number; // Absolute X displacement from start (currentCoords.x - startCoords.x)
180+
diffY: number; // Absolute Y displacement from start (currentCoords.y - startCoords.y)
181+
182+
deltaX: number; // Incremental X change since last frame (currentCoords.x - prevCoords.x)
183+
deltaY: number; // Incremental Y change since last frame (currentCoords.y - prevCoords.y)
184+
}, event: MouseEvent) => void;
185+
```
186+
187+
- Use `diffX`/`diffY` when you need to calculate position relative to drag start (e.g., `initialPosition + diffX`)
188+
- Use `deltaX`/`deltaY` when you need frame-to-frame movement (e.g., `currentPosition + deltaX`)
189+
166190
### 4. Reactive Data with Signal Subscriptions
167191

168192
GraphComponent enables reactive programming with a simple subscription system:
@@ -461,13 +485,14 @@ class BadgeComponent extends GraphComponent<BadgeComponentProps, BadgeComponentS
461485
}
462486
});
463487

464-
// Make it draggable
488+
// Make it draggable using incremental delta values
465489
this.onDrag({
466490
isDraggable: () => !this.props.locked,
467-
onDragUpdate: ({ diffX, diffY }) => {
491+
onDragUpdate: ({ deltaX, deltaY }) => {
492+
// deltaX/deltaY provide frame-to-frame movement
468493
this.setState({
469-
x: this.state.x - diffX,
470-
y: this.state.y - diffY
494+
x: this.state.x + deltaX,
495+
y: this.state.y + deltaY
471496
});
472497
this.updateHitBox();
473498
}
@@ -953,13 +978,14 @@ class EditorNode extends GraphComponent {
953978
constructor(props, parent) {
954979
super(props, parent);
955980

956-
// Make node draggable
981+
// Make node draggable using incremental delta values
957982
this.onDrag({
958983
isDraggable: () => this.context.editMode === 'move',
959-
onDragUpdate: ({ diffX, diffY }) => {
984+
onDragUpdate: ({ deltaX, deltaY }) => {
985+
// deltaX/deltaY provide frame-to-frame movement
960986
this.setState({
961-
x: this.state.x - diffX,
962-
y: this.state.y - diffY
987+
x: this.state.x + deltaX,
988+
y: this.state.y + deltaY
963989
});
964990
}
965991
});

src/components/canvas/GraphComponent/index.tsx

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,13 @@ export class GraphComponent<
117117
onDragStart?: (_event: MouseEvent) => void | boolean;
118118
onDragUpdate?: (
119119
diff: {
120+
startCoords: [number, number];
120121
prevCoords: [number, number];
121122
currentCoords: [number, number];
122123
diffX: number;
123124
diffY: number;
125+
deltaX: number;
126+
deltaY: number;
124127
},
125128
_event: MouseEvent
126129
) => void;
@@ -129,7 +132,8 @@ export class GraphComponent<
129132
autopanning?: boolean;
130133
dragCursor?: CursorLayerCursorTypes;
131134
}) {
132-
let startDragCoords: [number, number];
135+
let startCoords: [number, number];
136+
let prevCoords: [number, number];
133137
return this.addEventListener("mousedown", (event: MouseEvent) => {
134138
if (!isDraggable?.(event)) {
135139
return;
@@ -146,22 +150,29 @@ export class GraphComponent<
146150
return;
147151
}
148152
const xy = getXY(this.context.canvas, event);
149-
startDragCoords = this.context.camera.applyToPoint(xy[0], xy[1]);
153+
startCoords = this.context.camera.applyToPoint(xy[0], xy[1]);
154+
prevCoords = startCoords;
150155
})
151156
.on(EVENTS.DRAG_UPDATE, (event: MouseEvent) => {
152-
if (!startDragCoords.length) return;
157+
if (!startCoords?.length) return;
153158

154159
const [canvasX, canvasY] = getXY(this.context.canvas, event);
155160
const currentCoords = this.context.camera.applyToPoint(canvasX, canvasY);
156161

157-
const diffX = (startDragCoords[0] - currentCoords[0]) | 0;
158-
const diffY = (startDragCoords[1] - currentCoords[1]) | 0;
162+
// Absolute diff from drag start
163+
const diffX = currentCoords[0] - startCoords[0];
164+
const diffY = currentCoords[1] - startCoords[1];
159165

160-
onDragUpdate?.({ prevCoords: startDragCoords, currentCoords, diffX, diffY }, event);
161-
startDragCoords = currentCoords;
166+
// Incremental diff from previous frame
167+
const deltaX = currentCoords[0] - prevCoords[0];
168+
const deltaY = currentCoords[1] - prevCoords[1];
169+
170+
onDragUpdate?.({ startCoords, prevCoords, currentCoords, diffX, diffY, deltaX, deltaY }, event);
171+
prevCoords = currentCoords;
162172
})
163173
.on(EVENTS.DRAG_END, (_event: MouseEvent) => {
164-
startDragCoords = undefined;
174+
startCoords = undefined;
175+
prevCoords = undefined;
165176
onDrop?.(_event);
166177
});
167178
});

src/components/canvas/groups/BlockGroups.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,12 @@ export class BlockGroups<P extends BlockGroupsProps = BlockGroupsProps> extends
111111
return this.props.graph.getGraphLayer().$.camera as CoreComponent;
112112
}
113113

114-
public updateBlocks = (groupId: TGroupId, { diffX, diffY }: { diffX: number; diffY: number }) => {
114+
public updateBlocks = (groupId: TGroupId, { deltaX, deltaY }: { deltaX: number; deltaY: number }) => {
115115
if ((this.props as BlockGroupsProps & { updateBlocksOnDrag?: boolean }).updateBlocksOnDrag) {
116116
const blocks = this.$groupsBlocksMap.value[groupId];
117117
if (blocks) {
118118
blocks.forEach((block) => {
119-
block.updateXY(block.x - diffX, block.y - diffY, true);
119+
block.updateXY(block.x + deltaX, block.y + deltaY, true);
120120
});
121121
}
122122
}

src/components/canvas/groups/Group.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export type TGroupGeometry = {
2626

2727
export type TGroupProps = {
2828
id: TGroupId;
29-
onDragUpdate: (groupId: string, diff: { diffX: number; diffY: number }) => void;
29+
onDragUpdate: (groupId: string, diff: { deltaX: number; deltaY: number }) => void;
3030
style?: Partial<TGroupStyle>;
3131
geometry?: Partial<TGroupGeometry>;
3232
draggable?: boolean;
@@ -114,18 +114,18 @@ export class Group<T extends TGroup = TGroup> extends GraphComponent<TGroupProps
114114
this.context.graph.cameraService.enableAutoPanning();
115115
this.context.graph.lockCursor("grabbing");
116116
},
117-
onDragUpdate: ({ diffX, diffY }) => {
117+
onDragUpdate: ({ deltaX, deltaY }) => {
118118
const rect = {
119-
x: this.state.rect.x - diffX,
120-
y: this.state.rect.y - diffY,
119+
x: this.state.rect.x + deltaX,
120+
y: this.state.rect.y + deltaY,
121121
width: this.state.rect.width,
122122
height: this.state.rect.height,
123123
};
124124
this.setState({
125125
rect,
126126
});
127127
this.updateHitBox(rect);
128-
this.props.onDragUpdate(this.props.id, { diffX, diffY });
128+
this.props.onDragUpdate(this.props.id, { deltaX, deltaY });
129129
},
130130
onDrop: () => {
131131
this.context.graph.cameraService.disableAutoPanning();

0 commit comments

Comments
 (0)