@samitouri / QOS-React / commits / 1fc7487eaa

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;