DOMMouseMoveTracker.js.flow 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. /**
  2. * Copyright (c) 2013-present, Facebook, Inc.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. *
  7. * This class listens to events on the document and then updates a react
  8. * component through callbacks.
  9. * Please note that captureMouseMove must be called in
  10. * order to initialize listeners on mousemove and mouseup.
  11. * releaseMouseMove must be called to remove them. It is important to
  12. * call releaseMouseMoves since mousemove is expensive to listen to.
  13. *
  14. * @providesModule DOMMouseMoveTracker
  15. * @typechecks
  16. */
  17. 'use strict';
  18. const EventListener = require("./EventListener");
  19. const cancelAnimationFramePolyfill = require("./cancelAnimationFramePolyfill");
  20. const requestAnimationFramePolyfill = require("./requestAnimationFramePolyfill");
  21. class DOMMouseMoveTracker {
  22. /**
  23. * onMove is the callback that will be called on every mouse move.
  24. * onMoveEnd is called on mouse up when movement has ended.
  25. */
  26. constructor(
  27. /*function*/
  28. onMove,
  29. /*function*/
  30. onMoveEnd,
  31. /*DOMElement*/
  32. domNode) {
  33. this._isDragging = false;
  34. this._animationFrameID = null;
  35. this._domNode = domNode;
  36. this._onMove = onMove;
  37. this._onMoveEnd = onMoveEnd;
  38. this._onMouseMove = this._onMouseMove.bind(this);
  39. this._onMouseUp = this._onMouseUp.bind(this);
  40. this._didMouseMove = this._didMouseMove.bind(this);
  41. }
  42. /**
  43. * This is to set up the listeners for listening to mouse move
  44. * and mouse up signaling the movement has ended. Please note that these
  45. * listeners are added at the document.body level. It takes in an event
  46. * in order to grab inital state.
  47. */
  48. captureMouseMoves(
  49. /*object*/
  50. event) {
  51. if (!this._eventMoveToken && !this._eventUpToken) {
  52. this._eventMoveToken = EventListener.listen(this._domNode, 'mousemove', this._onMouseMove);
  53. this._eventUpToken = EventListener.listen(this._domNode, 'mouseup', this._onMouseUp);
  54. }
  55. if (!this._isDragging) {
  56. this._deltaX = 0;
  57. this._deltaY = 0;
  58. this._isDragging = true;
  59. this._x = event.clientX;
  60. this._y = event.clientY;
  61. }
  62. event.preventDefault();
  63. }
  64. /**
  65. * These releases all of the listeners on document.body.
  66. */
  67. releaseMouseMoves() {
  68. if (this._eventMoveToken && this._eventUpToken) {
  69. this._eventMoveToken.remove();
  70. this._eventMoveToken = null;
  71. this._eventUpToken.remove();
  72. this._eventUpToken = null;
  73. }
  74. if (this._animationFrameID !== null) {
  75. cancelAnimationFramePolyfill(this._animationFrameID);
  76. this._animationFrameID = null;
  77. }
  78. if (this._isDragging) {
  79. this._isDragging = false;
  80. this._x = null;
  81. this._y = null;
  82. }
  83. }
  84. /**
  85. * Returns whether or not if the mouse movement is being tracked.
  86. */
  87. isDragging()
  88. /*boolean*/
  89. {
  90. return this._isDragging;
  91. }
  92. /**
  93. * Calls onMove passed into constructor and updates internal state.
  94. */
  95. _onMouseMove(
  96. /*object*/
  97. event) {
  98. const x = event.clientX;
  99. const y = event.clientY;
  100. this._deltaX += x - this._x;
  101. this._deltaY += y - this._y;
  102. if (this._animationFrameID === null) {
  103. // The mouse may move faster then the animation frame does.
  104. // Use `requestAnimationFramePolyfill` to avoid over-updating.
  105. this._animationFrameID = requestAnimationFramePolyfill(this._didMouseMove);
  106. }
  107. this._x = x;
  108. this._y = y;
  109. event.preventDefault();
  110. }
  111. _didMouseMove() {
  112. this._animationFrameID = null;
  113. this._onMove(this._deltaX, this._deltaY);
  114. this._deltaX = 0;
  115. this._deltaY = 0;
  116. }
  117. /**
  118. * Calls onMoveEnd passed into constructor and updates internal state.
  119. */
  120. _onMouseUp() {
  121. if (this._animationFrameID) {
  122. this._didMouseMove();
  123. }
  124. this._onMoveEnd();
  125. }
  126. }
  127. module.exports = DOMMouseMoveTracker;