DOMMouseMoveTracker.js.flow 3.78 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
/**
 * Copyright (c) 2013-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * This class listens to events on the document and then updates a react
 * component through callbacks.
 * Please note that captureMouseMove must be called in
 * order to initialize listeners on mousemove and mouseup.
 * releaseMouseMove must be called to remove them. It is important to
 * call releaseMouseMoves since mousemove is expensive to listen to.
 *
 * @providesModule DOMMouseMoveTracker
 * @typechecks
 */
'use strict';

const EventListener = require("./EventListener");

const cancelAnimationFramePolyfill = require("./cancelAnimationFramePolyfill");

const requestAnimationFramePolyfill = require("./requestAnimationFramePolyfill");

class DOMMouseMoveTracker {
  /**
   * onMove is the callback that will be called on every mouse move.
   * onMoveEnd is called on mouse up when movement has ended.
   */
  constructor(
  /*function*/
  onMove,
  /*function*/
  onMoveEnd,
  /*DOMElement*/
  domNode) {
    this._isDragging = false;
    this._animationFrameID = null;
    this._domNode = domNode;
    this._onMove = onMove;
    this._onMoveEnd = onMoveEnd;
    this._onMouseMove = this._onMouseMove.bind(this);
    this._onMouseUp = this._onMouseUp.bind(this);
    this._didMouseMove = this._didMouseMove.bind(this);
  }
  /**
   * This is to set up the listeners for listening to mouse move
   * and mouse up signaling the movement has ended. Please note that these
   * listeners are added at the document.body level. It takes in an event
   * in order to grab inital state.
   */


  captureMouseMoves(
  /*object*/
  event) {
    if (!this._eventMoveToken && !this._eventUpToken) {
      this._eventMoveToken = EventListener.listen(this._domNode, 'mousemove', this._onMouseMove);
      this._eventUpToken = EventListener.listen(this._domNode, 'mouseup', this._onMouseUp);
    }

    if (!this._isDragging) {
      this._deltaX = 0;
      this._deltaY = 0;
      this._isDragging = true;
      this._x = event.clientX;
      this._y = event.clientY;
    }

    event.preventDefault();
  }
  /**
   * These releases all of the listeners on document.body.
   */


  releaseMouseMoves() {
    if (this._eventMoveToken && this._eventUpToken) {
      this._eventMoveToken.remove();

      this._eventMoveToken = null;

      this._eventUpToken.remove();

      this._eventUpToken = null;
    }

    if (this._animationFrameID !== null) {
      cancelAnimationFramePolyfill(this._animationFrameID);
      this._animationFrameID = null;
    }

    if (this._isDragging) {
      this._isDragging = false;
      this._x = null;
      this._y = null;
    }
  }
  /**
   * Returns whether or not if the mouse movement is being tracked.
   */


  isDragging()
  /*boolean*/
  {
    return this._isDragging;
  }
  /**
   * Calls onMove passed into constructor and updates internal state.
   */


  _onMouseMove(
  /*object*/
  event) {
    const x = event.clientX;
    const y = event.clientY;
    this._deltaX += x - this._x;
    this._deltaY += y - this._y;

    if (this._animationFrameID === null) {
      // The mouse may move faster then the animation frame does.
      // Use `requestAnimationFramePolyfill` to avoid over-updating.
      this._animationFrameID = requestAnimationFramePolyfill(this._didMouseMove);
    }

    this._x = x;
    this._y = y;
    event.preventDefault();
  }

  _didMouseMove() {
    this._animationFrameID = null;

    this._onMove(this._deltaX, this._deltaY);

    this._deltaX = 0;
    this._deltaY = 0;
  }
  /**
   * Calls onMoveEnd passed into constructor and updates internal state.
   */


  _onMouseUp() {
    if (this._animationFrameID) {
      this._didMouseMove();
    }

    this._onMoveEnd();
  }

}

module.exports = DOMMouseMoveTracker;