forked from angular/angular.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathswipe.js
137 lines (120 loc) · 4.87 KB
/
swipe.js
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
'use strict';
/**
* @ngdoc object
* @name ngTouch.$swipe
*
* @description
* The `$swipe` service is a service that abstracts the messier details of hold-and-drag swipe
* behavior, to make implementing swipe-related directives more convenient.
*
* Requires the {@link ngTouch `ngTouch`} module to be installed.
*
* `$swipe` is used by the `ngSwipeLeft` and `ngSwipeRight` directives in `ngTouch`, and by
* `ngCarousel` in a separate component.
*
* # Usage
* The `$swipe` service is an object with a single method: `bind`. `bind` takes an element
* which is to be watched for swipes, and an object with four handler functions. See the
* documentation for `bind` below.
*/
ngTouch.factory('$swipe', [function() {
// The total distance in any direction before we make the call on swipe vs. scroll.
var MOVE_BUFFER_RADIUS = 10;
function getCoordinates(event) {
var touches = event.touches && event.touches.length ? event.touches : [event];
var e = (event.changedTouches && event.changedTouches[0]) ||
(event.originalEvent && event.originalEvent.changedTouches &&
event.originalEvent.changedTouches[0]) ||
touches[0].originalEvent || touches[0];
return {
x: e.clientX,
y: e.clientY
};
}
return {
/**
* @ngdoc method
* @name ngTouch.$swipe#bind
* @methodOf ngTouch.$swipe
*
* @description
* The main method of `$swipe`. It takes an element to be watched for swipe motions, and an
* object containing event handlers.
*
* The four events are `start`, `move`, `end`, and `cancel`. `start`, `move`, and `end`
* receive as a parameter a coordinates object of the form `{ x: 150, y: 310 }`.
*
* `start` is called on either `mousedown` or `touchstart`. After this event, `$swipe` is
* watching for `touchmove` or `mousemove` events. These events are ignored until the total
* distance moved in either dimension exceeds a small threshold.
*
* Once this threshold is exceeded, either the horizontal or vertical delta is greater.
* - If the horizontal distance is greater, this is a swipe and `move` and `end` events follow.
* - If the vertical distance is greater, this is a scroll, and we let the browser take over.
* A `cancel` event is sent.
*
* `move` is called on `mousemove` and `touchmove` after the above logic has determined that
* a swipe is in progress.
*
* `end` is called when a swipe is successfully completed with a `touchend` or `mouseup`.
*
* `cancel` is called either on a `touchcancel` from the browser, or when we begin scrolling
* as described above.
*
*/
bind: function(element, eventHandlers) {
// Absolute total movement, used to control swipe vs. scroll.
var totalX, totalY;
// Coordinates of the start position.
var startCoords;
// Last event's position.
var lastPos;
// Whether a swipe is active.
var active = false;
element.on('touchstart mousedown', function(event) {
startCoords = getCoordinates(event);
active = true;
totalX = 0;
totalY = 0;
lastPos = startCoords;
eventHandlers['start'] && eventHandlers['start'](startCoords, event);
});
element.on('touchcancel', function(event) {
active = false;
eventHandlers['cancel'] && eventHandlers['cancel'](event);
});
element.on('touchmove mousemove', function(event) {
if (!active) return;
// Android will send a touchcancel if it thinks we're starting to scroll.
// So when the total distance (+ or - or both) exceeds 10px in either direction,
// we either:
// - On totalX > totalY, we send preventDefault() and treat this as a swipe.
// - On totalY > totalX, we let the browser handle it as a scroll.
if (!startCoords) return;
var coords = getCoordinates(event);
totalX += Math.abs(coords.x - lastPos.x);
totalY += Math.abs(coords.y - lastPos.y);
lastPos = coords;
if (totalX < MOVE_BUFFER_RADIUS && totalY < MOVE_BUFFER_RADIUS) {
return;
}
// One of totalX or totalY has exceeded the buffer, so decide on swipe vs. scroll.
if (totalY > totalX) {
// Allow native scrolling to take over.
active = false;
eventHandlers['cancel'] && eventHandlers['cancel'](event);
return;
} else {
// Prevent the browser from scrolling.
event.preventDefault();
eventHandlers['move'] && eventHandlers['move'](coords, event);
}
});
element.on('touchend mouseup', function(event) {
if (!active) return;
active = false;
eventHandlers['end'] && eventHandlers['end'](getCoordinates(event), event);
});
}
};
}]);