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