I have a simple looping animation in my component like this:
runAnimation() {
console.log('run animation');
this.state.angle.setValue(0);
Animated.timing(this.state.angle, {
toValue: 360,
duration: 8000,
easing: Easing.linear
}).start(() => this.runAnimation());
}
...
<Animated.Image
style={[
styles.rotate,
{ transform: [
{ rotate: this.state.angle.interpolate({
inputRange: [0, 360],
outputRange: ['0deg', '360deg']
})},
]}
]}
source={require('./spinning_ball.png')}
/>
How would I stop this animation? For example, when navigating away to another screen or after a user clicks on a button.
I tried using this.state.angle.stopAnimation() but noticed run animation still being printed in the console. Is there a different stop method I should be calling to prevent the start callback from being executed?
If an animation is in process of being animated and, for any particular reason, you need to stop it, you can call stopAnimation . The stopAnimation call also takes a callback with the value that the animation was stopped on.
Right-Click and Select Freeze Frame. Or from menu "Layer->Time->FreezeFrame". your layer will freeze at this point.. you can extend or trim this layer as long as you want.
start() Animations are started by calling start() on your animation. start() takes a completion callback that will be called when the animation is done or when the animation is done because stop() was called on it before it could finish.
Looping an animation causes it to repeat. You can loop animations in Advanced mode. Each element's animation can be looped separately, or you can loop a more complex animation involving multiple elements.
Based on my comment in Nguyên Hoàng's answer. Here is another way to stop the looping animation if you call this.state.angle.stopAnimation()
:
runAnimation() {
this.state.angle.setValue(0);
Animated.timing(this.state.angle, {
toValue: 360,
duration: 8000,
easing: Easing.linear
}).start((o) => {
if(o.finished) {
this.runAnimation();
}
});
}
I like to encapsulate my animations in hooks, it makes for a much cleaner, easier, and more reusable pattern than class components. Here's how I do a looping animation with easy start/stop control in typescript:
export const useBounceRotateAnimation = (running: boolean = true, rate: number = 300) => {
//Example of making an infinite looping animation and controlling it with a boolean
//Note that this assumes the "rate" is constant -- if you wanted to change the rate value after creation the implementation would have to change a bit
//Only create the animated value once
const val = useRef(new Animated.Value(0))
//Store a reference to the animation since we're starting-stopping an infinite loop instead of starting new animations and we aren't changing any animation values after creation. We only want to create the animation object once.
const anim = useRef(
Animated.loop(
Animated.timing(val.current, {
toValue: 1,
duration: rate,
easing: Easing.linear,
useNativeDriver: true,
isInteraction: false,
})
)
).current
//Interpolate the value(s) to whatever is appropriate for your case
const interpolatedY = val.current.interpolate({
inputRange: [0, 0.5, 1],
outputRange: [0, 6, 0],
})
const interpolatedRotate = val.current.interpolate({
inputRange: [0, 0.25, 0.5, 1],
outputRange: ["0deg", "-3deg", "3deg", "0deg"],
})
//Start and stop the animation based on the value of the boolean prop
useEffect(() => {
if (running) {
anim.start()
} else {
//When stopping reset the value to 0 so animated item doesn't stop in a random position
anim.stop()
val.current.setValue(0)
}
//Return a function from useEffect to stop the animation on unmount
return () => anim.stop()
//This useEffect should rerun if "running" or "anim" changes (but anim won't change since its a ref we never modify)
}, [running, anim])
//Return the animated values. Use "as const" const assertion to narrow the output type to exactly the two values being returned.
return [interpolatedY, interpolatedRotate] as const
}
I use this actual hook implementation to make a character image "walk" in my game. Now in my component it's very easy to use:
const MyComponent = () => {
const [isRunning, setIsRunning] = useState(true)
const [translation, rotation] = useBounceRotateAnimation(isRunning)
return (
<Animated.View
style={{
transform: [{ translateY: translation}, { rotate: rotation}],
}}
>
....rest of your component here, just setIsRunning(false) to stop animation whenever you need
</Animated.View>
)
}
As you can see this pattern is very clean and reusable. The animated component doesn't need to know anything about the animation, it just consumes the animated values and tells the animation when to run.
You can create a variable stopAnimation
to stop Animation whenever you want by only when stopAnimation === false
then callback runAnimation
function. Like example:
this.state = { stopAnimation: false }
runAnimation() {
this.state.spinValue.setValue(0);
Animated.timing(
this.state.spinValue,
{
toValue: 1,
duration: 3000,
easing: Easing.linear
}
).start( () => {
if(this.state.stopAnimation === false) {
this.runAnimation();
}
});
}
So you just need to create a button which call function this.state = { stopAnimation: true }
to stop Animation.
Example here: https://rnplay.org/apps/Lpmh8A.
You can stop the loop using:
componentWillUnmount() {
Animated.timing(this.state.angle).stop();
}
The simplest way to stop looping animation by calling setValue()
method and assign a value. like:
const [opacity, setOpacity] = useState(new Animated.value(0));
// declare your looping animated function
// Any time to stop the looping animated function just call setValue()
// on your stop animation event or function using opcacity reference. like
opacity.setValue(0)
For more details https://reactnative.dev/docs/animatedvaluexy#setvalue
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