From fa8c536b31cbfec3868bbf36d63c605baa8c0183 Mon Sep 17 00:00:00 2001 From: Aria Buckles Date: Mon, 14 Nov 2016 09:29:18 -0800 Subject: [PATCH] TouchableOpacity: Respond instantly to first touch Summary: On iOS, when you press down native fading components, they become transparent instantly, but then have an animated fade in/out if you move your finger in/out of their hit box. On react-native currently, the touchdown fades, instead of providing instant feedback, which doesn't feel right on iOS. I'm less familiar with Android conventions, but it seems to use fading components for buttons less often, instead using the ripple effect from TouchableNativeFeedback. In either case, instant feedback seems better for the user. Closes https://github.com/facebook/react-native/pull/10866 Differential Revision: D4175854 Pulled By: hramos fbshipit-source-id: d993231074e8190cf4ba7ca86dc24299f05d5d8f --- .../Animated/src/AnimatedImplementation.js | 5 ++++- .../Components/Touchable/TouchableOpacity.js | 19 ++++++++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/Libraries/Animated/src/AnimatedImplementation.js b/Libraries/Animated/src/AnimatedImplementation.js index b1d84d2a7..948967f72 100644 --- a/Libraries/Animated/src/AnimatedImplementation.js +++ b/Libraries/Animated/src/AnimatedImplementation.js @@ -302,7 +302,10 @@ class TimingAnimation extends Animation { this.__onEnd = onEnd; var start = () => { - if (this._duration === 0) { + // Animations that sometimes have 0 duration and sometimes do not + // still need to use the native driver when duration is 0 so as to + // not cause intermixed JS and native animations. + if (this._duration === 0 && !this._useNativeDriver) { this._onUpdate(this._toValue); this.__debouncedOnEnd({finished: true}); } else { diff --git a/Libraries/Components/Touchable/TouchableOpacity.js b/Libraries/Components/Touchable/TouchableOpacity.js index 929d72ef6..02b5d8395 100644 --- a/Libraries/Components/Touchable/TouchableOpacity.js +++ b/Libraries/Components/Touchable/TouchableOpacity.js @@ -84,10 +84,10 @@ var TouchableOpacity = React.createClass({ /** * Animate the touchable to a new opacity. */ - setOpacityTo: function(value: number) { + setOpacityTo: function(value: number, duration: number = 150) { Animated.timing( this.state.anim, - {toValue: value, duration: 150, useNativeDriver: true} + {toValue: value, duration: duration, useNativeDriver: true} ).start(); }, @@ -98,7 +98,11 @@ var TouchableOpacity = React.createClass({ touchableHandleActivePressIn: function(e: Event) { this.clearTimeout(this._hideTimeout); this._hideTimeout = null; - this._opacityActive(); + if (e.dispatchConfig.registrationName === 'onResponderGrant') { + this._opacityActive(0); + } else { + this._opacityActive(150); + } this.props.onPressIn && this.props.onPressIn(e); }, @@ -111,7 +115,7 @@ var TouchableOpacity = React.createClass({ touchableHandlePress: function(e: Event) { this.clearTimeout(this._hideTimeout); - this._opacityActive(); + this._opacityActive(150); this._hideTimeout = this.setTimeout( this._opacityInactive, this.props.delayPressOut || 100 @@ -144,8 +148,8 @@ var TouchableOpacity = React.createClass({ return this.props.delayPressOut; }, - _opacityActive: function() { - this.setOpacityTo(this.props.activeOpacity); + _opacityActive: function(duration: number) { + this.setOpacityTo(this.props.activeOpacity, duration); }, _opacityInactive: function() { @@ -153,7 +157,8 @@ var TouchableOpacity = React.createClass({ this._hideTimeout = null; var childStyle = flattenStyle(this.props.style) || {}; this.setOpacityTo( - childStyle.opacity === undefined ? 1 : childStyle.opacity + childStyle.opacity === undefined ? 1 : childStyle.opacity, + 150 ); },