Spaces:
Running
Running
File size: 4,278 Bytes
6bcb42f |
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 |
import bindAll from 'lodash.bindall';
import {getEventXY} from '../lib/touch-utils';
class DragRecognizer {
/* Gesture states */
static get STATE_UNIDENTIFIED () {
return 'unidentified';
}
static get STATE_SCROLL () {
return 'scroll';
}
static get STATE_DRAG () {
return 'drag';
}
constructor ({
onDrag = (() => {}),
onDragEnd = (() => {}),
touchDragAngle = 70, // Angle and distance thresholds are the same as scratch-blocks
distanceThreshold = 3
}) {
this._onDrag = onDrag;
this._onDragEnd = onDragEnd;
this._touchDragAngle = touchDragAngle;
this._distanceThreshold = distanceThreshold;
this._initialOffset = null;
this._gestureState = DragRecognizer.STATE_UNIDENTIFIED;
bindAll(this, [
'start',
'gestureInProgress',
'reset',
'_handleMove',
'_handleEnd'
]);
}
start (event) {
if (typeof event.button === 'number' && event.button !== 0) {
return;
}
this._initialOffset = getEventXY(event);
this._bindListeners();
}
gestureInProgress () {
return this._gestureState !== DragRecognizer.STATE_UNIDENTIFIED;
}
reset () {
this._unbindListeners();
this._initialOffset = null;
this._gestureState = DragRecognizer.STATE_UNIDENTIFIED;
}
//
// Internal functions
//
_bindListeners () {
window.addEventListener('mouseup', this._handleEnd);
window.addEventListener('mousemove', this._handleMove);
window.addEventListener('touchend', this._handleEnd);
// touchmove must be marked as non-passive, or else it cannot prevent scrolling
window.addEventListener('touchmove', this._handleMove, {passive: false});
}
_unbindListeners () {
window.removeEventListener('mouseup', this._handleEnd);
window.removeEventListener('mousemove', this._handleMove);
window.removeEventListener('touchend', this._handleEnd);
window.removeEventListener('touchmove', this._handleMove, {passive: false});
}
_handleMove (event) {
// For gestures identified as vertical scrolls, do not process movement events
if (this._isScroll()) return;
const currentOffset = getEventXY(event);
// Try to identify this gesture if it hasn't been identified already
if (!this.gestureInProgress()) {
const dx = currentOffset.x - this._initialOffset.x;
const dy = currentOffset.y - this._initialOffset.y;
const dragDistance = Math.sqrt((dx * dx) + (dy * dy));
if (dragDistance < this._distanceThreshold) return;
// For touch moves, additionally check if the angle suggests drag vs. scroll
if (event.type === 'touchmove') {
// Direction goes from -180 to 180, with 0 toward the right.
let angle = Math.atan2(dy, dx) / Math.PI * 180;
// Fold over horizontal axis, range now 0 to 180
angle = Math.abs(angle);
// Fold over vertical axis, range now 0 to 90
if (angle > 90) angle = 180 - angle;
if (angle > this._touchDragAngle) {
this._gestureState = DragRecognizer.STATE_SCROLL;
} else {
this._gestureState = DragRecognizer.STATE_DRAG;
}
} else {
// Mouse moves are always considered drags
this._gestureState = DragRecognizer.STATE_DRAG;
}
}
if (this._isDrag()) {
this._onDrag(currentOffset, this._initialOffset);
event.preventDefault();
}
}
_handleEnd () {
this.reset();
// Call the callback after reset to make sure if gestureInProgress()
// is used in response, it get the correct value (i.e. no gesture in progress)
this._onDragEnd();
}
_isDrag () {
return this._gestureState === DragRecognizer.STATE_DRAG;
}
_isScroll () {
return this._gestureState === DragRecognizer.STATE_SCROLL;
}
}
export default DragRecognizer;
|