Skip to content

Commit 2d4fe86

Browse files
committed
Fix native TouchableOpacity implementation
1 parent 011fff7 commit 2d4fe86

File tree

1 file changed

+38
-52
lines changed

1 file changed

+38
-52
lines changed

src/incubator/TouchableOpacity.js

Lines changed: 38 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// TODO: support hitSlop
22
// TODO: fix issue where passing backgroundColor thru style doesn't work
33
// TODO: fix issue with the default value of feedbackColor 'transparent'
4-
import React, {Component} from 'react';
4+
import React, {PureComponent} from 'react';
55
import {processColor} from 'react-native';
66
import PropTypes from 'prop-types';
77
import _ from 'lodash';
@@ -14,21 +14,21 @@ const {
1414
Code,
1515
cond,
1616
and,
17-
not,
1817
eq,
18+
neq,
19+
interpolate,
20+
Extrapolate,
1921
Value,
2022
call,
2123
block,
2224
event,
2325
timing,
24-
debug,
25-
clockRunning,
2626
set,
2727
startClock,
2828
stopClock
2929
} = Reanimated;
3030

31-
class TouchableOpacity extends Component {
31+
class TouchableOpacity extends PureComponent {
3232
static propTypes = {
3333
backgroundColor: PropTypes.string,
3434
feedbackColor: PropTypes.string,
@@ -48,13 +48,12 @@ class TouchableOpacity extends Component {
4848
pressState: new Value(-1)
4949
};
5050

51+
_prevPressState = new Value(-1);
5152
isAnimating = new Value(0);
5253
clock = new Clock();
53-
_scale = new Value(1);
54-
// _color = new Value(1);
55-
56-
_opacity = block([cond(eq(this.pressState, State.BEGAN), this.props.activeOpacity, 1)]);
5754

55+
_scale = runTiming(this.clock, this.pressState, this.props.activeScale, 1);
56+
_opacity = runTiming(this.clock, this.pressState, this.props.activeOpacity, 1);
5857
_color = cond(eq(this.pressState, State.BEGAN),
5958
processColor(this.props.feedbackColor || this.backgroundColor),
6059
processColor(this.backgroundColor));
@@ -91,16 +90,11 @@ class TouchableOpacity extends Component {
9190
{useNativeDriver: true});
9291

9392
render() {
94-
const {modifiers, style, activeScale, onPress, forwardedRef, ...others} = this.props;
93+
const {modifiers, style, onPress, forwardedRef, ...others} = this.props;
9594
const {borderRadius, paddings, margins, alignments, flexStyle, backgroundColor} = modifiers;
9695

9796
return (
98-
<TapGestureHandler
99-
onHandlerStateChange={this.onStateChange}
100-
shouldCancelWhenOutside
101-
ref={forwardedRef}
102-
maxDurationMs={500}
103-
>
97+
<TapGestureHandler onHandlerStateChange={this.onStateChange} shouldCancelWhenOutside ref={forwardedRef}>
10498
<Reanimated.View
10599
{...others}
106100
style={[
@@ -112,39 +106,30 @@ class TouchableOpacity extends Component {
112106
backgroundColor && {backgroundColor},
113107
style,
114108
this.animatedStyle
115-
// {backgroundColor: this._color, opacity: this._opacity, transform: [{scale: this._scale}]}
116109
]}
117110
>
118111
{this.props.children}
119112

120113
<Code>
121-
{() =>
122-
block([
123-
// trigger onPress callback on END state once
124-
cond(and(eq(this.isAnimating, 0), eq(this.pressState, State.END)), [
125-
set(this.isAnimating, 1),
126-
114+
{() => {
115+
return block([
116+
cond(and(eq(this.pressState, State.END), eq(this._prevPressState, State.BEGAN)), [
127117
call([], () => onPress(this.props))
128118
]),
129-
// Active state - scale animation
130-
cond(eq(this.pressState, State.BEGAN), block([runTiming(this.clock, this._scale, 1, activeScale)])),
131-
// End state - scale animation
132-
cond(eq(this.pressState, State.END), block([runTiming(this.clock, this._scale, activeScale, 1)])),
133-
// Reset isAnimating flag
134-
cond(and(eq(this.pressState, State.END), not(clockRunning(this.clock))), set(this.isAnimating, 0))
135-
])
136-
}
119+
set(this._prevPressState, this.pressState)
120+
]);
121+
}}
137122
</Code>
138123
</Reanimated.View>
139124
</TapGestureHandler>
140125
);
141126
}
142127
}
143128

144-
function runTiming(clock, position, value, dest) {
129+
function runTiming(clock, gestureState, initialValue, endValue) {
145130
const state = {
146131
finished: new Value(0),
147-
position,
132+
position: new Value(0),
148133
time: new Value(0),
149134
frameTime: new Value(0)
150135
};
@@ -156,26 +141,27 @@ function runTiming(clock, position, value, dest) {
156141
};
157142

158143
return block([
159-
cond(clockRunning(clock),
160-
[
161-
// if the clock is already running we update the toValue, in case a new dest has been passed in
162-
set(config.toValue, dest)
163-
],
164-
[
165-
// if the clock isn't running we reset all the animation params and start the clock
166-
set(state.finished, 0),
167-
set(state.time, 0),
168-
set(state.position, value),
169-
set(state.frameTime, 0),
170-
set(config.toValue, dest),
171-
startClock(clock)
172-
]),
173-
// we run the step here that is going to update position
144+
cond(and(eq(gestureState, State.BEGAN), neq(config.toValue, 1)), [
145+
set(state.finished, 0),
146+
set(state.time, 0),
147+
set(state.frameTime, 0),
148+
set(config.toValue, 1),
149+
startClock(clock)
150+
]),
151+
cond(and(eq(gestureState, State.END), neq(config.toValue, 0)), [
152+
set(state.finished, 0),
153+
set(state.time, 0),
154+
set(state.frameTime, 0),
155+
set(config.toValue, 0),
156+
startClock(clock)
157+
]),
174158
timing(clock, state, config),
175-
// if the animation is over we stop the clock
176-
cond(state.finished, debug('stop clock', stopClock(clock))),
177-
// we made the block return the updated position
178-
state.position
159+
cond(state.finished, stopClock(clock)),
160+
interpolate(state.position, {
161+
inputRange: [0, 1],
162+
outputRange: [endValue, initialValue],
163+
extrapolate: Extrapolate.CLAMP
164+
})
179165
]);
180166
}
181167

0 commit comments

Comments
 (0)