Animation control, Clock reset
See original GitHub issueVersions “react”: “16.6.1”, “react-native”: “0.57.5”, “react-native-reanimated”: “^1.0.0-alpha.10”,
I want to be able to run and stop animations, while being able to listen to touches. Basically it’s the progress indicator in audio player, so if audio is playing, animation is playing, if user drags the indicator, animation stops and I respond to gestures.
But I am lost with just simple running / stopping the animation. This is the current behavior, the box jumps by value like it would move if clock was running all the time.
I think it’s because of the Clock? Below are two different versions In the first one, I create one instance of clock and “control” that. In the second one, I create new instance of the clock every time the runTiming() function is called, still I can stop it without any reference by stopClock(new Clock()). How does this work? What am I missing? I so confused.
// @flow
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import Animated, { Easing } from 'react-native-reanimated';
import React, { Component } from 'react';
const {
Value,
cond,
clockRunning,
startClock,
set,
timing,
debug,
stopClock,
block,
Clock,
eq,
call,
} = Animated;
function runTiming(clock, value, dest) {
// clock = new Clock();
const state = {
finished: new Value(0),
position: new Value(0),
time: new Value(0),
frameTime: new Value(0),
};
const config = {
duration: 3000,
toValue: new Value(0),
easing: Easing.linear,
};
return block([
cond(clockRunning(clock), 0, [
set(state.finished, 0),
set(state.time, 0),
set(state.position, value),
set(state.frameTime, 0),
set(config.toValue, dest),
startClock(clock),
]),
timing(clock, state, config),
cond(state.finished, debug('stop clock', stopClock(clock))),
set(value, state.position),
]);
}
const PLAYER_STATE = {
PAUSED: 0,
PLAYING: 1,
};
class SimplePlayer extends Component<{}> {
clock: Clock;
trans: Value;
playingState: Value;
constructor() {
super();
this.clock = new Clock();
this.trans = new Value(0);
this.playingState = new Value(PLAYER_STATE.PAUSED);
}
render() {
return (
<View style={styles.container}>
<Animated.Code>
{() =>
block([
cond(eq(this.playingState, PLAYER_STATE.PLAYING), [
runTiming(this.clock, this.trans, 360),
]),
cond(eq(this.playingState, PLAYER_STATE.PAUSED), [
stopClock(this.clock),
]),
cond(eq(this.trans, 360), set(this.trans, 0)),
])
}
</Animated.Code>
<Animated.View
style={[styles.box, { transform: [{ translateY: this.trans }] }]}
/>
<TouchableOpacity
onPress={() => {
this.playingState.setValue(PLAYER_STATE.PLAYING);
}}
>
<Text>RUN ANIM</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
this.playingState.setValue(PLAYER_STATE.PAUSED);
}}
>
<Text>PAUSE ANIM</Text>
</TouchableOpacity>
</View>
);
}
}
const BOX_SIZE = 100;
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
box: {
width: BOX_SIZE,
height: BOX_SIZE,
borderColor: '#F5FCFF',
alignSelf: 'center',
backgroundColor: 'plum',
margin: BOX_SIZE / 2,
},
});
second version (shortened) still works like the previous
function runTiming(value, dest) {
const clock = new Clock(); // new clock instance
const state = {
finished: new Value(0),
position: new Value(0),
time: new Value(0),
frameTime: new Value(0),
};
const config = {
duration: 3000,
toValue: new Value(0),
easing: Easing.linear,
};
return block([
cond(clockRunning(clock), 0, [
set(state.finished, 0),
set(state.time, 0),
set(state.position, value),
set(state.frameTime, 0),
set(config.toValue, dest),
startClock(clock),
]),
timing(clock, state, config),
cond(state.finished, debug('stop clock', stopClock(clock))),
set(value, state.position),
]);
}
const PLAYER_STATE = {
PAUSED: 0,
PLAYING: 1,
};
class SimplePlayer extends Component<{}> {
trans: Value;
playingState: Value;
constructor() {
super();
// this.clock removed
this.trans = new Value(0);
this.playingState = new Value(PLAYER_STATE.PAUSED);
}
render() {
return (
<View style={styles.container}>
<Animated.Code>
{() =>
block([
cond(eq(this.playingState, PLAYER_STATE.PLAYING), [
runTiming(this.trans, 360),
]),
cond(eq(this.playingState, PLAYER_STATE.PAUSED), [
stopClock(new Clock()), // no reference to instance
]),
cond(eq(this.trans, 360), set(this.trans, 0)),
])
}
</Animated.Code>
<Animated.View
style={[styles.box, { transform: [{ translateY: this.trans }] }]}
/>
<TouchableOpacity
onPress={() => {
this.playingState.setValue(PLAYER_STATE.PLAYING);
}}
>
<Text>RUN ANIM</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
this.playingState.setValue(PLAYER_STATE.PAUSED);
}}
>
<Text>PAUSE ANIM</Text>
</TouchableOpacity>
</View>
);
}
}
Issue Analytics
- State:
- Created 5 years ago
- Reactions:6
- Comments:7 (1 by maintainers)
Top GitHub Comments
Hi guys,
Thanks @CptFabulouso, I had a similar issue (I guess), and indeed the problem for me was that on pausing I had to store clock’s value on
state.time
.Below a short example for those who might encounter the same issue (careful! shortened code…):
Cheers!
Hey! I believe you have found the solution already.
I am not entirely sure but from reading through the code example you posted it appears like you expect the clock to restart from the time at which you have stopped it. This is not how the clocks have been designed in reanimated to behave. The way clocks work is that they always have the value of the current system time. So even when you stop them they will keep giving you the right time at the moment of evaluation. The only consequence of starting/stopping the clock is that when the clock is stopped it won’t trigger update for the dependant nodes (that is the pieces of your reanimated code which use the clock). However, that code may still be reevaluated because the evaluation has been triggered by an event.
In short the solution is that you want to operate on time deltas. One think you can do is store the time when you start the clock in some state value. Then, instead of using the timestamp returned from clock you use diff between that timestamp and start timestamp.
I’m going to close this issue as 1) we want to migrate away from hosting the discussions on github issues and solely use it for bug reports and 2) I believe it is now resolved. In case it still isn’t clean for you feel free to reopen the issue as, sadly, it is unlikely anyone will answer here when the issue is closed.