I'm new to react-native-reanimated and trying to wrap my head around how it works. The code below renders a box in the middle of the screen. On the initial render, the box translates to the right for 4 seconds, after which its position is reset to the middle of the screen.
... imports omitted for brevity
export default class App extends React.Component {
state = {
clock: new Clock(),
translation: new Value(0),
};
onPress = () => {
startClock(this.state.clock);
};
getTranslation = (clock, translation) => {
const state = {
finished: new Value(0),
position: translation,
time: new Value(0),
frameTime: new Value(0),
};
const config = {
duration: 4000,
toValue: new Value(300),
easing: Easing.inOut(Easing.ease),
};
return block([
cond(clockRunning(clock), 0, [
set(state.finished, 0),
set(state.position, 0),
set(state.time, 0),
set(state.frameTime, 0),
startClock(clock),
]),
timing(clock, state, config),
cond(state.finished, set(state.position, 0)),
state.position,
]);
};
render() {
const translation = this.getTranslation(
this.state.clock,
this.state.translation
);
return (
<View style={styles.container}>
<TouchableOpacity onPress={this.onPress}>
<Animated.View
style={{
transform: [
{
translateX: translation,
},
],
width: 100,
height: 100,
backgroundColor: "tomato",
}}
/>
</TouchableOpacity>
</View>
);
}
}
My questions are:
1) Why does the box only translate to the right on the initial render? What prevents the animation from repeating?
2) The onPress handler doesn't restart the animation. Why?
I too am learning react native reanimated, but I will try to answer your questions. If anybody would like to edit the answer, please free to edit.
- Why does the box only translate to the right on the initial render? What prevents the animation from repeating?
When the component mounts, the translateX value is 0, which will be at the centre of the screen (because as you have told, the styles.container might have styles that centres its child component).
And since you are asking for the translation from the getTranslation function inside the render method, when the component renders for the first time, the getTranslation function is called and a block of nodes are evaluated:
block list executes, i.e., the cond block. It checks if the clock is running.
timing state and configuration. Please keep in mind that the state.position is set to 0 and also config.toValue is already set to 300. And start the clock.state.position to 0 and the config.toValue to 300, the state.position is updated to 1 (depends on the config.duration).finished value is 1.
position equals to the value of config's toValue, the timing function updates the value of finished to 1.position value is again set to 0. Meaning the position resets to its original position.On the last node, the state's position is returned to the render method to translation constant for the transform.
Since the clock is started, and animation is not finished, the block of nodes runs again, and again. Meaning on the next frame, the state.position value will be 2, and later 3, and so on until the state.position equals to 300 (config.toValue). So the box will go from the center to the right side the screen. If, however you would have set the config.toValue to -300, the box will go from center to the left side of the screen.
Finally, when the animation is finished, the 3rd node of the block equals to true, and the state.position is again to 0 (at the center of the screen).
And since you have not stopped the clock (which can be done with stopClock(clock)), the 1st node to check clockRunning(clock) is always true. Also to repeat the animation, you will have to reset all the timing state and config after the animation is finished.
So you have to change your block of nodes:
return block([
cond(
clockRunning(clock),
[
debug('clock is running', clock)
],
[
debug('clock is NOT running', clock),
startClock(clock),
]
),
timing(clock, state, config),
cond(
state.finished,
[
stopClock(clock),
set(state.finished, 0),
set(state.position, 0),
set(state.time, 0),
set(state.frameTime, 0),
startClock(clock),
]
),
state.position,
]);
- The onPress handler doesn't restart the animation. Why?
Because the clock was not stopped. Also because the state and config was not reset. So, to start the animation when pressed, this can be done in many different ways. I am going to show you how to in one of the ways, this way may be you will understand a bit more about react-native-reanimated and also react-native-gesture-handler to work with purely native animations, without crossing the bridge.
const getTranslation = ({clock, gestureState, translation}) => {
const state = {
finished: new Value(0),
position: translation,
time: new Value(0),
frameTime: new Value(0),
};
const config = {
duration: 2000,
toValue: new Value(150),
easing: Easing.inOut(Easing.ease),
};
return block([
cond(
clockRunning(clock),
[
debug('clock is running', clock)
],
[
debug('clock is NOT running', clock),
set(state.finished, 0),
set(state.position, 0),
set(state.time, 0),
set(state.frameTime, 0),
startClock(clock),
]
),
timing(clock, state, config),
cond(
state.finished,
stopClock(clock)
),
state.position
])
};
export default class App extends React.Component {
gestureState = new Value(-1)
clock = new Clock()
translation = cond(
eq(this.gestureState, State.ACTIVE), // when you start drag, the state will be ACTIVE
[
debug('active', this.gestureState, State.ACTIVE),
getTranslation({clock: this.clock, translation: new Value(0)})
],
[
debug('not active', this.gestureState, State.ACTIVE)
],
)
onStateChange = event([
{
nativeEvent: {
state: this.gestureState
}
}
])
render() {
return (
<View style={styles.container}>
<PanGestureHandler
onGestureChange={this.onStateChange}
onHandlerStateChange={this.onStateChange}>
<Animated.View style={[
styles.box,
{
transform: [{
translateX: this.translation
}]
}
]}/>
</PanGestureHandler>
</View>
)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With