@@ -60,7 +60,10 @@ type GridItemResizeCallback = (
60
60
type State = {
61
61
resizing : ?{ top : number , left : number , width : number , height : number } ,
62
62
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 }
64
67
} ;
65
68
66
69
type Props = {
@@ -442,7 +445,11 @@ export default class GridItem extends React.Component<Props, State> {
442
445
* @param {Event } e event data
443
446
* @param {Object } callbackData an object with node, delta and position information
444
447
*/
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
+ ) => {
446
453
const { onDragStart, transformScale } = this . props ;
447
454
if ( ! onDragStart ) return ;
448
455
@@ -459,7 +466,12 @@ export default class GridItem extends React.Component<Props, State> {
459
466
const pTop = parentRect . top / transformScale ;
460
467
newPosition . left = cLeft - pLeft + offsetParent . scrollLeft ;
461
468
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
+ } ) ;
463
475
464
476
// Call callback with this data
465
477
const { x, y } = calcXY (
@@ -484,16 +496,27 @@ export default class GridItem extends React.Component<Props, State> {
484
496
*/
485
497
onDrag : ( Event , ReactDraggableCallbackData ) = > void = (
486
498
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 }
488
501
) => {
489
502
const { onDrag } = this . props ;
490
503
if ( ! onDrag ) return ;
491
504
492
505
if ( ! this . state . dragging ) {
493
506
throw new Error ( "onDrag called before onDragStart." ) ;
494
507
}
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 ) ;
497
520
498
521
const { isBounded, i, w, h, containerWidth } = this . props ;
499
522
const positionParams = this . getPositionParams ( ) ;
0 commit comments