32 KiB
id, title, original_id
| id | title | original_id |
|---|---|---|
| version-0.21-panresponder | panresponder | panresponder |
PanResponder # | Edit on GitHub |
PanResponder reconciles several touches into a single gesture. It makes
single-touch gestures resilient to extra touches, and can be used to
recognize simple multi-touch gestures.
It provides a predictable wrapper of the responder handlers provided by the
gesture responder system.
For each handler, it provides a new gestureState object alongside the
native event object:
A native event is a synthetic touch event with the following form:
nativeEventchangedTouches- Array of all touch events that have changed since the last eventidentifier- The ID of the touchlocationX- The X position of the touch, relative to the elementlocationY- The Y position of the touch, relative to the elementpageX- The X position of the touch, relative to the root elementpageY- The Y position of the touch, relative to the root elementtarget- The node id of the element receiving the touch eventtimestamp- A time identifier for the touch, useful for velocity calculationtouches- Array of all current touches on the screen
A gestureState object has the following:
stateID- ID of the gestureState- persisted as long as there at least one touch on screenmoveX- the latest screen coordinates of the recently-moved touchmoveY- the latest screen coordinates of the recently-moved touchx0- the screen coordinates of the responder granty0- the screen coordinates of the responder grantdx- accumulated distance of the gesture since the touch starteddy- accumulated distance of the gesture since the touch startedvx- current velocity of the gesturevy- current velocity of the gesturenumberActiveTouches- Number of touches currently on screen
Basic Usage #
onPanResponderGrant<span class="token punctuation">:</span> <span class="token punctuation">(</span>evt<span class="token punctuation">,</span> gestureState<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">{</span>
<span class="token comment" spellcheck="true"> // The guesture has started. Show visual feedback so the user knows
// what is happening! // gestureState.{x,y}0 will be set to zero now }, onPanResponderMove: (evt, gestureState) => { // The most recent move distance is gestureState.move{X,Y} // The accumulated gesture distance since becoming responder is // gestureState.d{x,y} }, onPanResponderTerminationRequest: (evt, gestureState) => true, onPanResponderRelease: (evt, gestureState) => { // The user has released all touches while this view is the // responder. This typically means a gesture has succeeded }, onPanResponderTerminate: (evt, gestureState) => { // Another component has become the responder, so this gesture // should be cancelled }, onShouldBlockNativeResponder: (evt, gestureState) => { // Returns whether this component should block native components from becoming the JS // responder. Returns true by default. Is currently only supported on android. return true; }, }); },
render: function() { return ( <View {...this._panResponder.panHandlers} /> ); },
Working Example #
To see it in action, try the PanResponder example in UIExplorer
Methods #
static create(config: object) #
@param {object} config Enhanced versions of all of the responder callbacks
that provide not only the typical ResponderSyntheticEvent, but also the
PanResponder gesture state. Simply replace the word Responder with
PanResponder in each of the typical onResponder* callbacks. For
example, the config object would look like:
onMoveShouldSetPanResponder: (e, gestureState) => {...}onMoveShouldSetPanResponderCapture: (e, gestureState) => {...}onStartShouldSetPanResponder: (e, gestureState) => {...}onStartShouldSetPanResponderCapture: (e, gestureState) => {...}onPanResponderReject: (e, gestureState) => {...}onPanResponderGrant: (e, gestureState) => {...}onPanResponderStart: (e, gestureState) => {...}onPanResponderEnd: (e, gestureState) => {...}onPanResponderRelease: (e, gestureState) => {...}onPanResponderMove: (e, gestureState) => {...}onPanResponderTerminate: (e, gestureState) => {...}onPanResponderTerminationRequest: (e, gestureState) => {...}onShouldBlockNativeResponder: (e, gestureState) => {...}In general, for events that have capture equivalents, we update the gestureState once in the capture phase and can use it in the bubble phase as well.
Be careful with onStartShould* callbacks. They only reflect updated
gestureStatefor start/end events that bubble/capture to the Node. Once the node is the responder, you can rely on every start/end event being processed by the gesture andgestureStatebeing updated accordingly. (numberActiveTouches) may not be totally accurate unless you are the responder.
Examples # | Edit on GitHub |
var React = require('react-native'); var { PanResponder, StyleSheet, View, processColor, } = React;
var CIRCLE_SIZE = 80; var CIRCLE_COLOR = 'blue'; var CIRCLE_HIGHLIGHT_COLOR = 'green';
var PanResponderExample = React.createClass({
statics: { title: 'PanResponder Sample', description: 'Shows the use of PanResponder to provide basic gesture handling.', },
_panResponder: {}, _previousLeft: 0, _previousTop: 0, _circleStyles: {}, circle: (null : ?{ setNativeProps(props: Object): void }),
componentWillMount: function() { this._panResponder = PanResponder.create({ onStartShouldSetPanResponder: this._handleStartShouldSetPanResponder, onMoveShouldSetPanResponder: this._handleMoveShouldSetPanResponder, onPanResponderGrant: this._handlePanResponderGrant, onPanResponderMove: this._handlePanResponderMove, onPanResponderRelease: this._handlePanResponderEnd, onPanResponderTerminate: this._handlePanResponderEnd, }); this._previousLeft = 20; this._previousTop = 84; this._circleStyles = { style: { left: this._previousLeft, top: this._previousTop } }; },
componentDidMount: function() { this._updatePosition(); },
render: function() { return ( <View style={styles.container}> <View ref={(circle) => { this.circle = circle; }} style={styles.circle} {...this._panResponder.panHandlers} /> </View> ); },
_highlight: function() { const circle = this.circle; circle && circle.setNativeProps({ style: { backgroundColor: processColor(CIRCLE_HIGHLIGHT_COLOR) } }); },
_unHighlight: function() { const circle = this.circle; circle && circle.setNativeProps({ style: { backgroundColor: processColor(CIRCLE_COLOR) } }); },
_updatePosition: function() { this.circle && this.circle.setNativeProps(this._circleStyles); },
_handleStartShouldSetPanResponder: function(e: Object, gestureState: Object): boolean { // Should we become active when the user presses down on the circle? return true; },
_handleMoveShouldSetPanResponder: function(e: Object, gestureState: Object): boolean { // Should we become active when the user moves a touch over the circle? return true; },
_handlePanResponderGrant: function(e: Object, gestureState: Object) { this._highlight(); }, _handlePanResponderMove: function(e: Object, gestureState: Object) { this._circleStyles.style.left = this._previousLeft + gestureState.dx; this._circleStyles.style.top = this._previousTop + gestureState.dy; this._updatePosition(); }, _handlePanResponderEnd: function(e: Object, gestureState: Object) { this._unHighlight(); this._previousLeft += gestureState.dx; this._previousTop += gestureState.dy; }, });
var styles = StyleSheet.create({ circle: { width: CIRCLE_SIZE, height: CIRCLE_SIZE, borderRadius: CIRCLE_SIZE / 2, backgroundColor: CIRCLE_COLOR, position: 'absolute', left: 0, top: 0, }, container: { flex: 1, paddingTop: 64, }, });
module.exports = PanResponderExample;