Skip to content

Commit 7a575f4

Browse files
1 parent b29fce6 commit 7a575f4

File tree

2 files changed

+30
-7
lines changed

2 files changed

+30
-7
lines changed

lib/GridItem.jsx

+29-6
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,10 @@ type GridItemResizeCallback = (
6060
type State = {
6161
resizing: ?{ top: number, left: number, width: number, height: number },
6262
dragging: ?{ top: number, left: number },
63-
className: string
63+
className: string,
64+
// We add this field to remember the position at start of dragging
65+
widgetRectangleStart: ?Position,
66+
handlePositionStart: ?{ x: number, y: number }
6467
};
6568

6669
type Props = {
@@ -442,7 +445,11 @@ export default class GridItem extends React.Component<Props, State> {
442445
* @param {Event} e event data
443446
* @param {Object} callbackData an object with node, delta and position information
444447
*/
445-
onDragStart: (Event, ReactDraggableCallbackData) => void = (e, { node }) => {
448+
// Extracting `lastX` and `lastY` from passed object (see longer comment below)
449+
onDragStart: (Event, ReactDraggableCallbackData) => void = (
450+
e,
451+
{ node, lastX, lastY }
452+
) => {
446453
const { onDragStart, transformScale } = this.props;
447454
if (!onDragStart) return;
448455

@@ -459,7 +466,12 @@ export default class GridItem extends React.Component<Props, State> {
459466
const pTop = parentRect.top / transformScale;
460467
newPosition.left = cLeft - pLeft + offsetParent.scrollLeft;
461468
newPosition.top = cTop - pTop + offsetParent.scrollTop;
462-
this.setState({ dragging: newPosition });
469+
this.setState({
470+
dragging: newPosition,
471+
// Remembering state at start of dragging
472+
widgetRectangleStart: newPosition,
473+
handlePositionStart: { x: lastX, y: lastY }
474+
});
463475

464476
// Call callback with this data
465477
const { x, y } = calcXY(
@@ -484,16 +496,27 @@ export default class GridItem extends React.Component<Props, State> {
484496
*/
485497
onDrag: (Event, ReactDraggableCallbackData) => void = (
486498
e,
487-
{ node, deltaX, deltaY }
499+
// Extracting `lastX` and `lastY` from passed object rather than `deltaX` and `deltaY` (see longer comment below)
500+
{ node, lastX, lastY }
488501
) => {
489502
const { onDrag } = this.props;
490503
if (!onDrag) return;
491504

492505
if (!this.state.dragging) {
493506
throw new Error("onDrag called before onDragStart.");
494507
}
495-
let top = this.state.dragging.top + deltaY;
496-
let left = this.state.dragging.left + deltaX;
508+
// In order to fix an issue where dragging does not correctly follow the mouse cursor, we use the `lastX` and
509+
// `lastY` properties of the mouse event rather than the `deltaX` and `deltaY` properties.
510+
//
511+
// This approach is more robust because `lastX` and `lastY` are absolute values and will remain true even if some
512+
// events get dropped, whereas calculating the resulting position with `deltaX` and `deltaY` in a cumulative way
513+
// will accumulate errors if some events are skipped.
514+
let top =
515+
this.state.widgetRectangleStart.top +
516+
(lastY - this.state.handlePositionStart.y);
517+
let left =
518+
this.state.widgetRectangleStart.left +
519+
(lastX - this.state.handlePositionStart.x);
497520

498521
const { isBounded, i, w, h, containerWidth } = this.props;
499522
const positionParams = this.getPositionParams();

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"fast-equals": "^4.0.3",
4242
"prop-types": "^15.8.1",
4343
"react-draggable": "^4.4.5",
44-
"react-resizable": "^3.0.5",
44+
"react-resizable": "3.0.5",
4545
"resize-observer-polyfill": "^1.5.1"
4646
},
4747
"_dependencyNotes": {

0 commit comments

Comments
 (0)