Move PanResponder back to react-native repo temporarily.
We have some more work to do with this before we can pull it out of react-native. FB Task #10926500
Spencer Ahrens committed
Apr 22, 2016 at 16:37 UTC
1fc7487eaae85faedf1836078ecc3c5bef8db947
1 file changed
-379
src/renderers/shared/event/eventPlugins/PanResponder.js
deleted
-379
@@ -1,379 +0,0 @@
1
-/**
2
- * @providesModule PanResponder
3
- */
4
-
5
-'use strict';
6
-
7
-var TouchHistoryMath = require('TouchHistoryMath');
8
-
9
-var currentCentroidXOfTouchesChangedAfter =
10
- TouchHistoryMath.currentCentroidXOfTouchesChangedAfter;
11
-var currentCentroidYOfTouchesChangedAfter =
12
- TouchHistoryMath.currentCentroidYOfTouchesChangedAfter;
13
-var previousCentroidXOfTouchesChangedAfter =
14
- TouchHistoryMath.previousCentroidXOfTouchesChangedAfter;
15
-var previousCentroidYOfTouchesChangedAfter =
16
- TouchHistoryMath.previousCentroidYOfTouchesChangedAfter;
17
-var currentCentroidX = TouchHistoryMath.currentCentroidX;
18
-var currentCentroidY = TouchHistoryMath.currentCentroidY;
19
-
20
-/**
21
- * `PanResponder` reconciles several touches into a single gesture. It makes
22
- * single-touch gestures resilient to extra touches, and can be used to
23
- * recognize simple multi-touch gestures.
24
- *
25
- * It provides a predictable wrapper of the responder handlers provided by the
26
- * [gesture responder system](docs/gesture-responder-system.html).
27
- * For each handler, it provides a new `gestureState` object alongside the
28
- * native event object:
29
- *
30
- * ```
31
- * onPanResponderMove: (event, gestureState) => {}
32
- * ```
33
- *
34
- * A native event is a synthetic touch event with the following form:
35
- *
36
- * - `nativeEvent`
37
- * + `changedTouches` - Array of all touch events that have changed since the last event
38
- * + `identifier` - The ID of the touch
39
- * + `locationX` - The X position of the touch, relative to the element
40
- * + `locationY` - The Y position of the touch, relative to the element
41
- * + `pageX` - The X position of the touch, relative to the root element
42
- * + `pageY` - The Y position of the touch, relative to the root element
43
- * + `target` - The node id of the element receiving the touch event
44
- * + `timestamp` - A time identifier for the touch, useful for velocity calculation
45
- * + `touches` - Array of all current touches on the screen
46
- *
47
- * A `gestureState` object has the following:
48
- *
49
- * - `stateID` - ID of the gestureState- persisted as long as there at least
50
- * one touch on screen
51
- * - `moveX` - the latest screen coordinates of the recently-moved touch
52
- * - `moveY` - the latest screen coordinates of the recently-moved touch
53
- * - `x0` - the screen coordinates of the responder grant
54
- * - `y0` - the screen coordinates of the responder grant
55
- * - `dx` - accumulated distance of the gesture since the touch started
56
- * - `dy` - accumulated distance of the gesture since the touch started
57
- * - `vx` - current velocity of the gesture
58
- * - `vy` - current velocity of the gesture
59
- * - `numberActiveTouches` - Number of touches currently on screen
60
- *
61
- * ### Basic Usage
62
- *
63
- * ```
64
- * componentWillMount: function() {
65
- * this._panResponder = PanResponder.create({
66
- * // Ask to be the responder:
67
- * onStartShouldSetPanResponder: (evt, gestureState) => true,
68
- * onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
69
- * onMoveShouldSetPanResponder: (evt, gestureState) => true,
70
- * onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
71
- *
72
- * onPanResponderGrant: (evt, gestureState) => {
73
- * // The guesture has started. Show visual feedback so the user knows
74
- * // what is happening!
75
- *
76
- * // gestureState.{x,y}0 will be set to zero now
77
- * },
78
- * onPanResponderMove: (evt, gestureState) => {
79
- * // The most recent move distance is gestureState.move{X,Y}
80
- *
81
- * // The accumulated gesture distance since becoming responder is
82
- * // gestureState.d{x,y}
83
- * },
84
- * onPanResponderTerminationRequest: (evt, gestureState) => true,
85
- * onPanResponderRelease: (evt, gestureState) => {
86
- * // The user has released all touches while this view is the
87
- * // responder. This typically means a gesture has succeeded
88
- * },
89
- * onPanResponderTerminate: (evt, gestureState) => {
90
- * // Another component has become the responder, so this gesture
91
- * // should be cancelled
92
- * },
93
- * onShouldBlockNativeResponder: (evt, gestureState) => {
94
- * // Returns whether this component should block native components from becoming the JS
95
- * // responder. Returns true by default. Is currently only supported on android.
96
- * return true;
97
- * },
98
- * });
99
- * },
100
- *
101
- * render: function() {
102
- * return (
103
- * <View {...this._panResponder.panHandlers} />
104
- * );
105
- * },
106
- *
107
- * ```
108
- *
109
- * ### Working Example
110
- *
111
- * To see it in action, try the
112
- * [PanResponder example in UIExplorer](https://github.com/facebook/react-native/blob/master/Examples/UIExplorer/PanResponderExample.js)
113
- */
114
-
115
-var PanResponder = {
116
-
117
- /**
118
- *
119
- * A graphical explanation of the touch data flow:
120
- *
121
- * +----------------------------+ +--------------------------------+
122
- * | ResponderTouchHistoryStore | |TouchHistoryMath |
123
- * +----------------------------+ +----------+---------------------+
124
- * |Global store of touchHistory| |Allocation-less math util |
125
- * |including activeness, start | |on touch history (centroids |
126
- * |position, prev/cur position.| |and multitouch movement etc) |
127
- * | | | |
128
- * +----^-----------------------+ +----^---------------------------+
129
- * | |
130
- * | (records relevant history |
131
- * | of touches relevant for |
132
- * | implementing higher level |
133
- * | gestures) |
134
- * | |
135
- * +----+-----------------------+ +----|---------------------------+
136
- * | ResponderEventPlugin | | | Your App/Component |
137
- * +----------------------------+ +----|---------------------------+
138
- * |Negotiates which view gets | Low level | | High level |
139
- * |onResponderMove events. | events w/ | +-+-------+ events w/ |
140
- * |Also records history into | touchHistory| | Pan | multitouch + |
141
- * |ResponderTouchHistoryStore. +---------------->Responder+-----> accumulative|
142
- * +----------------------------+ attached to | | | distance and |
143
- * each event | +---------+ velocity. |
144
- * | |
145
- * | |
146
- * +--------------------------------+
147
- *
148
- *
149
- *
150
- * Gesture that calculates cumulative movement over time in a way that just
151
- * "does the right thing" for multiple touches. The "right thing" is very
152
- * nuanced. When moving two touches in opposite directions, the cumulative
153
- * distance is zero in each dimension. When two touches move in parallel five
154
- * pixels in the same direction, the cumulative distance is five, not ten. If
155
- * two touches start, one moves five in a direction, then stops and the other
156
- * touch moves fives in the same direction, the cumulative distance is ten.
157
- *
158
- * This logic requires a kind of processing of time "clusters" of touch events
159
- * so that two touch moves that essentially occur in parallel but move every
160
- * other frame respectively, are considered part of the same movement.
161
- *
162
- * Explanation of some of the non-obvious fields:
163
- *
164
- * - moveX/moveY: If no move event has been observed, then `(moveX, moveY)` is
165
- * invalid. If a move event has been observed, `(moveX, moveY)` is the
166
- * centroid of the most recently moved "cluster" of active touches.
167
- * (Currently all move have the same timeStamp, but later we should add some
168
- * threshold for what is considered to be "moving"). If a palm is
169
- * accidentally counted as a touch, but a finger is moving greatly, the palm
170
- * will move slightly, but we only want to count the single moving touch.
171
- * - x0/y0: Centroid location (non-cumulative) at the time of becoming
172
- * responder.
173
- * - dx/dy: Cumulative touch distance - not the same thing as sum of each touch
174
- * distance. Accounts for touch moves that are clustered together in time,
175
- * moving the same direction. Only valid when currently responder (otherwise,
176
- * it only represents the drag distance below the threshold).
177
- * - vx/vy: Velocity.
178
- */
179
-
180
- _initializeGestureState: function(gestureState) {
181
- gestureState.moveX = 0;
182
- gestureState.moveY = 0;
183
- gestureState.x0 = 0;
184
- gestureState.y0 = 0;
185
- gestureState.dx = 0;
186
- gestureState.dy = 0;
187
- gestureState.vx = 0;
188
- gestureState.vy = 0;
189
- gestureState.numberActiveTouches = 0;
190
- // All `gestureState` accounts for timeStamps up until:
191
- gestureState._accountsForMovesUpTo = 0;
192
- },
193
-
194
- /**
195
- * This is nuanced and is necessary. It is incorrect to continuously take all
196
- * active *and* recently moved touches, find the centroid, and track how that
197
- * result changes over time. Instead, we must take all recently moved
198
- * touches, and calculate how the centroid has changed just for those
199
- * recently moved touches, and append that change to an accumulator. This is
200
- * to (at least) handle the case where the user is moving three fingers, and
201
- * then one of the fingers stops but the other two continue.
202
- *
203
- * This is very different than taking all of the recently moved touches and
204
- * storing their centroid as `dx/dy`. For correctness, we must *accumulate
205
- * changes* in the centroid of recently moved touches.
206
- *
207
- * There is also some nuance with how we handle multiple moved touches in a
208
- * single event. With the way `ReactNativeEventEmitter` dispatches touches as
209
- * individual events, multiple touches generate two 'move' events, each of
210
- * them triggering `onResponderMove`. But with the way `PanResponder` works,
211
- * all of the gesture inference is performed on the first dispatch, since it
212
- * looks at all of the touches (even the ones for which there hasn't been a
213
- * native dispatch yet). Therefore, `PanResponder` does not call
214
- * `onResponderMove` passed the first dispatch. This diverges from the
215
- * typical responder callback pattern (without using `PanResponder`), but
216
- * avoids more dispatches than necessary.
217
- */
218
- _updateGestureStateOnMove: function(gestureState, touchHistory) {
219
- gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
220
- gestureState.moveX = currentCentroidXOfTouchesChangedAfter(
221
- touchHistory,
222
- gestureState._accountsForMovesUpTo
223
- );
224
- gestureState.moveY = currentCentroidYOfTouchesChangedAfter(
225
- touchHistory,
226
- gestureState._accountsForMovesUpTo
227
- );
228
- var movedAfter = gestureState._accountsForMovesUpTo;
229
- var prevX = previousCentroidXOfTouchesChangedAfter(touchHistory, movedAfter);
230
- var x = currentCentroidXOfTouchesChangedAfter(touchHistory, movedAfter);
231
- var prevY = previousCentroidYOfTouchesChangedAfter(touchHistory, movedAfter);
232
- var y = currentCentroidYOfTouchesChangedAfter(touchHistory, movedAfter);
233
- var nextDX = gestureState.dx + (x - prevX);
234
- var nextDY = gestureState.dy + (y - prevY);
235
-
236
- // TODO: This must be filtered intelligently.
237
- var dt =
238
- (touchHistory.mostRecentTimeStamp - gestureState._accountsForMovesUpTo);
239
- gestureState.vx = (nextDX - gestureState.dx) / dt;
240
- gestureState.vy = (nextDY - gestureState.dy) / dt;
241
-
242
- gestureState.dx = nextDX;
243
- gestureState.dy = nextDY;
244
- gestureState._accountsForMovesUpTo = touchHistory.mostRecentTimeStamp;
245
- },
246
-
247
- /**
248
- * @param {object} config Enhanced versions of all of the responder callbacks
249
- * that provide not only the typical `ResponderSyntheticEvent`, but also the
250
- * `PanResponder` gesture state. Simply replace the word `Responder` with
251
- * `PanResponder` in each of the typical `onResponder*` callbacks. For
252
- * example, the `config` object would look like:
253
- *
254
- * - `onMoveShouldSetPanResponder: (e, gestureState) => {...}`
255
- * - `onMoveShouldSetPanResponderCapture: (e, gestureState) => {...}`
256
- * - `onStartShouldSetPanResponder: (e, gestureState) => {...}`
257
- * - `onStartShouldSetPanResponderCapture: (e, gestureState) => {...}`
258
- * - `onPanResponderReject: (e, gestureState) => {...}`
259
- * - `onPanResponderGrant: (e, gestureState) => {...}`
260
- * - `onPanResponderStart: (e, gestureState) => {...}`
261
- * - `onPanResponderEnd: (e, gestureState) => {...}`
262
- * - `onPanResponderRelease: (e, gestureState) => {...}`
263
- * - `onPanResponderMove: (e, gestureState) => {...}`
264
- * - `onPanResponderTerminate: (e, gestureState) => {...}`
265
- * - `onPanResponderTerminationRequest: (e, gestureState) => {...}`
266
- * - `onShouldBlockNativeResponder: (e, gestureState) => {...}`
267
- *
268
- * In general, for events that have capture equivalents, we update the
269
- * gestureState once in the capture phase and can use it in the bubble phase
270
- * as well.
271
- *
272
- * Be careful with onStartShould* callbacks. They only reflect updated
273
- * `gestureState` for start/end events that bubble/capture to the Node.
274
- * Once the node is the responder, you can rely on every start/end event
275
- * being processed by the gesture and `gestureState` being updated
276
- * accordingly. (numberActiveTouches) may not be totally accurate unless you
277
- * are the responder.
278
- */
279
- create: function(config) {
280
- var gestureState = {
281
- // Useful for debugging
282
- stateID: Math.random(),
283
- };
284
- PanResponder._initializeGestureState(gestureState);
285
- var panHandlers = {
286
- onStartShouldSetResponder: function(e) {
287
- return config.onStartShouldSetPanResponder === undefined ? false :
288
- config.onStartShouldSetPanResponder(e, gestureState);
289
- },
290
- onMoveShouldSetResponder: function(e) {
291
- return config.onMoveShouldSetPanResponder === undefined ? false :
292
- config.onMoveShouldSetPanResponder(e, gestureState);
293
- },
294
- onStartShouldSetResponderCapture: function(e) {
295
- // TODO: Actually, we should reinitialize the state any time
296
- // touches.length increases from 0 active to > 0 active.
297
- if (e.nativeEvent.touches.length === 1) {
298
- PanResponder._initializeGestureState(gestureState);
299
- }
300
- gestureState.numberActiveTouches = e.touchHistory.numberActiveTouches;
301
- return config.onStartShouldSetPanResponderCapture !== undefined ?
302
- config.onStartShouldSetPanResponderCapture(e, gestureState) : false;
303
- },
304
-
305
- onMoveShouldSetResponderCapture: function(e) {
306
- var touchHistory = e.touchHistory;
307
- // Responder system incorrectly dispatches should* to current responder
308
- // Filter out any touch moves past the first one - we would have
309
- // already processed multi-touch geometry during the first event.
310
- if (gestureState._accountsForMovesUpTo === touchHistory.mostRecentTimeStamp) {
311
- return false;
312
- }
313
- PanResponder._updateGestureStateOnMove(gestureState, touchHistory);
314
- return config.onMoveShouldSetPanResponderCapture ?
315
- config.onMoveShouldSetPanResponderCapture(e, gestureState) : false;
316
- },
317
-
318
- onResponderGrant: function(e) {
319
- gestureState.x0 = currentCentroidX(e.touchHistory);
320
- gestureState.y0 = currentCentroidY(e.touchHistory);
321
- gestureState.dx = 0;
322
- gestureState.dy = 0;
323
- if (config.onPanResponderGrant) config.onPanResponderGrant(e, gestureState);
324
- // TODO: t7467124 investigate if this can be removed
325
- return config.onShouldBlockNativeResponder === undefined ? true :
326
- config.onShouldBlockNativeResponder();
327
- },
328
-
329
- onResponderReject: function(e) {
330
- if (config.onPanResponderReject) config.onPanResponderReject(e, gestureState);
331
- },
332
-
333
- onResponderRelease: function(e) {
334
- if (config.onPanResponderRelease) config.onPanResponderRelease(e, gestureState);
335
- PanResponder._initializeGestureState(gestureState);
336
- },
337
-
338
- onResponderStart: function(e) {
339
- var touchHistory = e.touchHistory;
340
- gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
341
- if (config.onPanResponderStart) config.onPanResponderStart(e, gestureState);
342
- },
343
-
344
- onResponderMove: function(e) {
345
- var touchHistory = e.touchHistory;
346
- // Guard against the dispatch of two touch moves when there are two
347
- // simultaneously changed touches.
348
- if (gestureState._accountsForMovesUpTo === touchHistory.mostRecentTimeStamp) {
349
- return;
350
- }
351
- // Filter out any touch moves past the first one - we would have
352
- // already processed multi-touch geometry during the first event.
353
- PanResponder._updateGestureStateOnMove(gestureState, touchHistory);
354
- if (config.onPanResponderMove) config.onPanResponderMove(e, gestureState);
355
- },
356
-
357
- onResponderEnd: function(e) {
358
- var touchHistory = e.touchHistory;
359
- gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
360
- if (config.onPanResponderEnd) config.onPanResponderEnd(e, gestureState);
361
- },
362
-
363
- onResponderTerminate: function(e) {
364
- if (config.onPanResponderTerminate) {
365
- config.onPanResponderTerminate(e, gestureState);
366
- }
367
- PanResponder._initializeGestureState(gestureState);
368
- },
369
-
370
- onResponderTerminationRequest: function(e) {
371
- return config.onPanResponderTerminationRequest === undefined ? true :
372
- config.onPanResponderTerminationRequest(e, gestureState);
373
- },
374
- };
375
- return {panHandlers: panHandlers};
376
- },
377
-};
378
-
379
-module.exports = PanResponder;