From 41f2169629976fe41f1636314218fb574573cac5 Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Tue, 13 Dec 2016 11:21:48 -0800 Subject: [PATCH] Improve TouchableOpacity Summary: I was comparing `` to `UIButton` in iOS and it just doesn't feel native. The initial delay was fixed by https://github.com/facebook/react-native/pull/10866 but still there is a lag between button release and animation. I'm also not sure what `_hideTimeout` was used for. When logging `touchableHandle*` events looks like `touchableHandleActivePressIn` is called first, then `touchableHandleActivePressOut` and then `touchableHandlePress`. Which means the fade in animation from `touchableHandleActivePressOut` was interrupted by `touchableHandlePress`. Reviewed By: vjeux Differential Revision: D4309789 fbshipit-source-id: b6d4df544952e11c2ade97d860531cbb2fada36b --- .../Components/Touchable/TouchableOpacity.js | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/Libraries/Components/Touchable/TouchableOpacity.js b/Libraries/Components/Touchable/TouchableOpacity.js index 02b5d8395..fe6a3fd22 100644 --- a/Libraries/Components/Touchable/TouchableOpacity.js +++ b/Libraries/Components/Touchable/TouchableOpacity.js @@ -14,6 +14,7 @@ // Note (avik): add @flow when Flow supports spread properties in propTypes var Animated = require('Animated'); +var Easing = require('Easing'); var NativeMethodsMixin = require('NativeMethodsMixin'); var React = require('React'); var TimerMixin = require('react-timer-mixin'); @@ -84,10 +85,15 @@ var TouchableOpacity = React.createClass({ /** * Animate the touchable to a new opacity. */ - setOpacityTo: function(value: number, duration: number = 150) { + setOpacityTo: function(value: number, duration: number) { Animated.timing( this.state.anim, - {toValue: value, duration: duration, useNativeDriver: true} + { + toValue: value, + duration: duration, + easing: Easing.inOut(Easing.quad), + useNativeDriver: true, + } ).start(); }, @@ -96,8 +102,6 @@ var TouchableOpacity = React.createClass({ * defined on your component. */ touchableHandleActivePressIn: function(e: Event) { - this.clearTimeout(this._hideTimeout); - this._hideTimeout = null; if (e.dispatchConfig.registrationName === 'onResponderGrant') { this._opacityActive(0); } else { @@ -107,19 +111,11 @@ var TouchableOpacity = React.createClass({ }, touchableHandleActivePressOut: function(e: Event) { - if (!this._hideTimeout) { - this._opacityInactive(); - } + this._opacityInactive(250); this.props.onPressOut && this.props.onPressOut(e); }, touchableHandlePress: function(e: Event) { - this.clearTimeout(this._hideTimeout); - this._opacityActive(150); - this._hideTimeout = this.setTimeout( - this._opacityInactive, - this.props.delayPressOut || 100 - ); this.props.onPress && this.props.onPress(e); }, @@ -152,13 +148,11 @@ var TouchableOpacity = React.createClass({ this.setOpacityTo(this.props.activeOpacity, duration); }, - _opacityInactive: function() { - this.clearTimeout(this._hideTimeout); - this._hideTimeout = null; + _opacityInactive: function(duration: number) { var childStyle = flattenStyle(this.props.style) || {}; this.setOpacityTo( childStyle.opacity === undefined ? 1 : childStyle.opacity, - 150 + duration ); },