diff --git a/packages/react-dom/src/events/SyntheticEvent.js b/packages/react-dom/src/events/SyntheticEvent.js index 01742c705278c..81c4728bce3b4 100644 --- a/packages/react-dom/src/events/SyntheticEvent.js +++ b/packages/react-dom/src/events/SyntheticEvent.js @@ -152,11 +152,22 @@ export const UIEventInterface: EventInterfaceType = { detail: 0, }; -let previousScreenX = 0; -let previousScreenY = 0; -// Use flags to signal movementX/Y has already been set -let isMovementXSet = false; -let isMovementYSet = false; +let lastMovementX; +let lastMovementY; +let previousMouseEvent; + +function updateMouseMovementPolyfillState(event) { + if (event !== previousMouseEvent) { + if (previousMouseEvent && event.type === 'mousemove') { + lastMovementX = event.screenX - previousMouseEvent.screenX; + lastMovementY = event.screenY - previousMouseEvent.screenY; + } else { + lastMovementX = 0; + lastMovementY = 0; + } + previousMouseEvent = event; + } +} /** * @interface MouseEvent @@ -189,31 +200,15 @@ export const MouseEventInterface: EventInterfaceType = { if ('movementX' in event) { return event.movementX; } - - const screenX = previousScreenX; - previousScreenX = event.screenX; - - if (!isMovementXSet) { - isMovementXSet = true; - return 0; - } - - return event.type === 'mousemove' ? event.screenX - screenX : 0; + updateMouseMovementPolyfillState(event); + return lastMovementX; }, movementY: function(event) { if ('movementY' in event) { return event.movementY; } - - const screenY = previousScreenY; - previousScreenY = event.screenY; - - if (!isMovementYSet) { - isMovementYSet = true; - return 0; - } - - return event.type === 'mousemove' ? event.screenY - screenY : 0; + updateMouseMovementPolyfillState(event); + return lastMovementY; }, }; diff --git a/packages/react-dom/src/events/__tests__/SyntheticMouseEvent-test.js b/packages/react-dom/src/events/__tests__/SyntheticMouseEvent-test.js index 46a502e38e06e..26412eac52dfa 100644 --- a/packages/react-dom/src/events/__tests__/SyntheticMouseEvent-test.js +++ b/packages/react-dom/src/events/__tests__/SyntheticMouseEvent-test.js @@ -16,6 +16,7 @@ describe('SyntheticMouseEvent', () => { let container; beforeEach(() => { + jest.resetModules(); React = require('react'); ReactDOM = require('react-dom'); @@ -77,4 +78,67 @@ describe('SyntheticMouseEvent', () => { expect(events[1]).toBe(6); expect(events[2]).toBe(0); // mousedown event should have movementX at 0 }); + + it('should correctly calculate movementX/Y for capture phase', () => { + const events = []; + const onMouseMove = event => { + events.push(['move', false, event.movementX, event.movementY]); + }; + const onMouseMoveCapture = event => { + events.push(['move', true, event.movementX, event.movementY]); + }; + const onMouseDown = event => { + events.push(['down', false, event.movementX, event.movementY]); + }; + const onMouseDownCapture = event => { + events.push(['down', true, event.movementX, event.movementY]); + }; + + const node = ReactDOM.render( +
, + container, + ); + + let event = new MouseEvent('mousemove', { + relatedTarget: null, + bubbles: true, + screenX: 2, + screenY: 2, + }); + + node.dispatchEvent(event); + + event = new MouseEvent('mousemove', { + relatedTarget: null, + bubbles: true, + screenX: 8, + screenY: 9, + }); + + node.dispatchEvent(event); + + // Now trigger a mousedown event to see if movementX has changed back to 0 + event = new MouseEvent('mousedown', { + relatedTarget: null, + bubbles: true, + screenX: 25, + screenY: 65, + }); + + node.dispatchEvent(event); + + expect(events).toEqual([ + ['move', true, 0, 0], + ['move', false, 0, 0], + ['move', true, 6, 7], + ['move', false, 6, 7], + ['down', true, 0, 0], + ['down', false, 0, 0], + ]); + }); });