2015-03-10 14:23:03 -07:00
/ * *
2015-03-23 13:35:08 -07:00
* Copyright ( c ) 2015 - present , Facebook , Inc .
* All rights reserved .
*
* This source code is licensed under the BSD - style license found in the
* LICENSE file in the root directory of this source tree . An additional grant
* of patent rights can be found in the PATENTS file in the same directory .
2015-03-10 14:23:03 -07:00
*
* @ providesModule TouchableBounce
2015-03-25 12:55:10 -07:00
* @ flow
2015-03-10 14:23:03 -07:00
* /
'use strict' ;
2015-05-02 09:46:49 -07:00
var AnimationExperimental = require ( 'AnimationExperimental' ) ;
2015-03-10 14:23:03 -07:00
var NativeMethodsMixin = require ( 'NativeMethodsMixin' ) ;
var POPAnimation = require ( 'POPAnimation' ) ;
2015-05-02 09:46:49 -07:00
var React = require ( 'React' ) ;
2015-03-10 14:23:03 -07:00
var Touchable = require ( 'Touchable' ) ;
var merge = require ( 'merge' ) ;
var onlyChild = require ( 'onlyChild' ) ;
2015-06-28 10:58:26 -07:00
var invariant = require ( 'invariant' ) ;
invariant (
AnimationExperimental || POPAnimation ,
'Please add the RCTAnimationExperimental framework to your project, or add //Libraries/FBReactKit:RCTPOPAnimation to your BUCK file if running internally within Facebook.'
) ;
2015-03-25 12:55:10 -07:00
type State = {
2015-04-08 14:09:24 -07:00
animationID : ? number ;
2015-03-25 12:55:10 -07:00
} ;
2015-03-10 14:23:03 -07:00
/ * *
* When the scroll view is disabled , this defines how far your touch may move
* off of the button , before deactivating the button . Once deactivated , try
* moving it back and you ' ll see that the button is once again reactivated !
* Move it back and forth several times while the scroll view is disabled .
* /
var PRESS _RECT _OFFSET = { top : 20 , left : 20 , right : 20 , bottom : 30 } ;
/ * *
* Example of using the ` TouchableMixin ` to play well with other responder
* locking views including ` ScrollView ` . ` TouchableMixin ` provides touchable
* hooks ( ` this.touchableHandle* ` ) that we forward events to . In turn ,
* ` TouchableMixin ` expects us to implement some abstract methods to handle
* interesting interactions such as ` handleTouchablePress ` .
* /
var TouchableBounce = React . createClass ( {
mixins : [ Touchable . Mixin , NativeMethodsMixin ] ,
propTypes : {
onPress : React . PropTypes . func ,
// The function passed takes a callback to start the animation which should
// be run after this onPress handler is done. You can use this (for example)
// to update UI before starting the animation.
onPressWithCompletion : React . PropTypes . func ,
// the function passed is called after the animation is complete
onPressAnimationComplete : React . PropTypes . func ,
} ,
2015-03-25 12:55:10 -07:00
getInitialState : function ( ) : State {
2015-03-10 14:23:03 -07:00
return merge ( this . touchableGetInitialState ( ) , { animationID : null } ) ;
} ,
2015-03-25 12:55:10 -07:00
bounceTo : function (
value : number ,
velocity : number ,
bounciness : number ,
2015-04-08 14:09:24 -07:00
fromValue ? : ? number ,
2015-03-25 12:55:10 -07:00
callback ? : ? Function
) {
2015-03-10 14:23:03 -07:00
if ( POPAnimation ) {
this . state . animationID && this . removeAnimation ( this . state . animationID ) ;
var anim = {
property : POPAnimation . Properties . scaleXY ,
dynamicsTension : 0 ,
toValue : [ value , value ] ,
velocity : [ velocity , velocity ] ,
springBounciness : bounciness ,
2015-04-08 14:09:24 -07:00
fromValue : fromValue ? [ fromValue , fromValue ] : undefined ,
2015-03-10 14:23:03 -07:00
} ;
this . state . animationID = POPAnimation . createSpringAnimation ( anim ) ;
this . addAnimation ( this . state . animationID , callback ) ;
} else {
2015-04-08 14:09:24 -07:00
AnimationExperimental . startAnimation (
{
node : this ,
duration : 300 ,
easing : 'easeOutBack' ,
property : 'scaleXY' ,
toValue : { x : value , y : value } ,
} ,
callback
) ;
2015-03-10 14:23:03 -07:00
}
} ,
/ * *
* ` Touchable.Mixin ` self callbacks . The mixin will invoke these if they are
* defined on your component .
* /
touchableHandleActivePressIn : function ( ) {
this . bounceTo ( 0.93 , 0.1 , 0 ) ;
} ,
touchableHandleActivePressOut : function ( ) {
this . bounceTo ( 1 , 0.4 , 0 ) ;
} ,
touchableHandlePress : function ( ) {
2015-03-25 12:55:10 -07:00
var onPressWithCompletion = this . props . onPressWithCompletion ;
if ( onPressWithCompletion ) {
onPressWithCompletion (
2015-03-10 14:23:03 -07:00
this . bounceTo . bind ( this , 1 , 10 , 10 , 0.93 , this . props . onPressAnimationComplete )
) ;
return ;
}
this . bounceTo ( 1 , 10 , 10 , undefined , this . props . onPressAnimationComplete ) ;
this . props . onPress && this . props . onPress ( ) ;
} ,
2015-03-25 12:55:10 -07:00
touchableGetPressRectOffset : function ( ) : typeof PRESS _RECT _OFFSET {
2015-03-10 14:23:03 -07:00
return PRESS _RECT _OFFSET ; // Always make sure to predeclare a constant!
} ,
2015-03-25 12:55:10 -07:00
touchableGetHighlightDelayMS : function ( ) : number {
2015-03-10 14:23:03 -07:00
return 0 ;
} ,
render : function ( ) {
var child = onlyChild ( this . props . children ) ;
2015-05-02 09:46:49 -07:00
return React . cloneElement ( child , {
2015-03-10 14:23:03 -07:00
accessible : true ,
testID : this . props . testID ,
onStartShouldSetResponder : this . touchableHandleStartShouldSetResponder ,
onResponderTerminationRequest : this . touchableHandleResponderTerminationRequest ,
onResponderGrant : this . touchableHandleResponderGrant ,
onResponderMove : this . touchableHandleResponderMove ,
onResponderRelease : this . touchableHandleResponderRelease ,
onResponderTerminate : this . touchableHandleResponderTerminate
} ) ;
}
} ) ;
module . exports = TouchableBounce ;