I recently started trying to use animation in an app I am trying to make in React Native. I am not 100% familiar with React Animated, but I believe the animations that I am trying to make are very simple.
I have a screen where I would like some text to slide in from the right, pause a few seconds, slide out to the left, before repeating itself with some other text.
While I did manage to do that, the animation and the text quickly become very buggy (aka no smooth animation, no animation at all after a while, the text will very quickly change randomly, etc...).
I am not sure why this is, I tried switching the useNativeDriver
to true
to hopefully get a smoother animation, but then I get an error saying I can't use the style property 'left'.
Here is the code:
import React, { useState, useCallback, useEffect } from 'react';
import { Text, View, StyleSheet, Animated } from 'react-native';
import Constants from 'expo-constants';
function App() {
let [wordsAnim] = useState(new Animated.Value(60)),
runAnimation = () => {
wordsAnim.setValue(60);
Animated.sequence([
Animated.timing(wordsAnim, {
toValue: 0,
duration: 1500,
useNativeDriver: false,
}),
Animated.timing(wordsAnim, {
toValue: -60,
duration: 1500,
delay: 3000,
useNativeDriver: false,
}),
]).start(({ finished }) => {
runAnimation();
updateWord();
});
};
//An array of random words to display
const words = ['First', 'Second', 'Third', 'Fourth'];
//First word displayed is a random word from the array
const [word, changeWord] = useState(
words[Math.floor(Math.random() * words.length)]
);
//Update the word displayed with another random word from the array
const updateWord = () => {
const index = Math.floor(Math.random() * words.length);
changeWord(words[index]);
};
useEffect(() => {
runAnimation();
});
return (
<View style={styles.container}>
<Animated.Text
style={{
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
left: wordsAnim.interpolate({
inputRange: [0, 100],
outputRange: ['0%', '100%'],
}),
}}>
{word}
</Animated.Text>
</View>
);
}
export default App;
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
});
You can also find this example here.
I would also take any tips regarding animations or anything else for React Native, Thanks!!
As pointed out by @David784 , my runAnimation
was retriggered after every completed render. It should've only be run on component mounts.
Adding a few callbacks did the trick!
Working code below:
import React, { useState, useCallback, useEffect } from 'react';
import { Text, View, StyleSheet, Animated } from 'react-native';
import Constants from 'expo-constants';
function App() {
let [wordsAnim] = useState(new Animated.Value(60)),
runAnimation = useCallback(() => {
wordsAnim.setValue(60);
Animated.sequence([
Animated.timing(wordsAnim, {
toValue: 0,
duration: 1500,
useNativeDriver: false,
}),
Animated.timing(wordsAnim, {
toValue: -60,
duration: 1500,
delay: 3000,
useNativeDriver: false,
}),
]).start(({ finished }) => {
updateWord();
runAnimation();
});
}, [updateWord, wordsAnim]);
//An array of random words to display
const words = ['First', 'Second', 'Third', 'Fourth'];
//First word displayed is a random word from the array
const [word, changeWord] = useState(
words[Math.floor(Math.random() * words.length)]
);
//Update the word displayed with another random word from the array
const updateWord = useCallback(() => {
const index = Math.floor(Math.random() * words.length);
changeWord(words[index]);
}, [words]);
useEffect(() => {
runAnimation();
}, [runAnimation]);
return (
<View style={styles.container}>
<Animated.Text
style={{
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
left: wordsAnim.interpolate({
inputRange: [0, 100],
outputRange: ['0%', '100%'],
}),
}}>
{word}
</Animated.Text>
</View>
);
}
export default App;
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
});