Wat is Animated?
De Animated API is een krachtige library voor het maken van vloeiende animaties in React Native. Het biedt betere performance dan gewone state updates.
Waarom Animated gebruiken?
- Performance: Animaties lopen op de native thread (60 FPS)
- Smooth: Geen haperingen tijdens animaties
- Composable: Combineer meerdere animaties
- Interruptible: Animaties kunnen worden onderbroken
Animated Components:
Animated.View- Geanimeerde ViewAnimated.Text- Geanimeerde TextAnimated.Image- Geanimeerde ImageAnimated.ScrollView- Geanimeerde ScrollView
Basis Animatie
Elke animatie begint met een Animated.Value.
Fade In Animatie
import { useRef, useEffect } from 'react';
import { Animated, View, StyleSheet } from 'react-native';
export default function FadeInExample() {
const fadeAnim = useRef(new Animated.Value(0)).current;
useEffect(() => {
Animated.timing(fadeAnim, {
toValue: 1,
duration: 1000,
useNativeDriver: true,
}).start();
}, []);
return (
<View style={styles.container}>
<Animated.View style={[styles.box, { opacity: fadeAnim }]}>
<Animated.Text>Fade In!</Animated.Text>
</Animated.View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
box: {
width: 100,
height: 100,
backgroundColor: '#007AFF',
justifyContent: 'center',
alignItems: 'center',
},
});
Let op: Gebruik altijd useNativeDriver: true voor betere performance! Dit werkt voor opacity en transform, maar niet voor width/height/color.
Timing Animaties
Animated.timing() animaties veranderen een waarde over tijd.
Slide In Animatie
import { useRef, useEffect } from 'react';
import { Animated, View, StyleSheet } from 'react-native';
export default function SlideInExample() {
const slideAnim = useRef(new Animated.Value(-100)).current;
useEffect(() => {
Animated.timing(slideAnim, {
toValue: 0,
duration: 500,
useNativeDriver: true,
}).start();
}, []);
return (
<Animated.View
style={[
styles.box,
{ transform: [{ translateX: slideAnim }] }
]}
>
<Text>Slide In!</Text>
</Animated.View>
);
}
const styles = StyleSheet.create({
box: {
width: 200,
height: 100,
backgroundColor: '#007AFF',
justifyContent: 'center',
alignItems: 'center',
marginTop: 100,
},
});
Met Button Trigger
import { useRef } from 'react';
import { Animated, Pressable, Text, View, StyleSheet } from 'react-native';
export default function AnimatedButton() {
const scaleAnim = useRef(new Animated.Value(1)).current;
const animatePress = () => {
Animated.sequence([
Animated.timing(scaleAnim, {
toValue: 0.8,
duration: 100,
useNativeDriver: true,
}),
Animated.timing(scaleAnim, {
toValue: 1,
duration: 100,
useNativeDriver: true,
}),
]).start();
};
return (
<Pressable onPress={animatePress}>
<Animated.View
style={[
styles.button,
{ transform: [{ scale: scaleAnim }] }
]}
>
<Text style={styles.buttonText}>Druk mij!</Text>
</Animated.View>
</Pressable>
);
}
const styles = StyleSheet.create({
button: {
backgroundColor: '#007AFF',
padding: 15,
borderRadius: 8,
alignItems: 'center',
},
buttonText: {
color: 'white',
fontWeight: 'bold',
},
});
Spring Animaties
Animated.spring() maakt een verende animatie (bouncy effect).
Bounce Animatie
import { useRef } from 'react';
import { Animated, Pressable, StyleSheet } from 'react-native';
export default function BounceExample() {
const bounceAnim = useRef(new Animated.Value(0)).current;
const bounce = () => {
bounceAnim.setValue(0);
Animated.spring(bounceAnim, {
toValue: 1,
friction: 2, // Hoe "stug" de veer is
tension: 40, // Hoe snel de veer terug springt
useNativeDriver: true,
}).start();
};
return (
<Pressable onPress={bounce}>
<Animated.View
style={[
styles.box,
{
transform: [
{ scale: bounceAnim },
{
translateY: bounceAnim.interpolate({
inputRange: [0, 1],
outputRange: [0, -50],
})
}
],
},
]}
>
<Text>Bounce!</Text>
</Animated.View>
</Pressable>
);
}
const styles = StyleSheet.create({
box: {
width: 100,
height: 100,
backgroundColor: '#007AFF',
justifyContent: 'center',
alignItems: 'center',
borderRadius: 8,
},
});
Spring Properties:
friction- Weerstand (standaard: 7, hoger = minder bounce)tension- Spanning (standaard: 40, hoger = sneller)speed- Algemene snelheidbounciness- Hoe bouncy (alternatief voor friction)
Interpolate
interpolate() zet één animated value om naar meerdere waarden (zoals kleur, rotatie, etc.).
Rotatie Animatie
import { useRef, useEffect } from 'react';
import { Animated, View, StyleSheet } from 'react-native';
export default function RotateExample() {
const rotateAnim = useRef(new Animated.Value(0)).current;
useEffect(() => {
Animated.loop(
Animated.timing(rotateAnim, {
toValue: 1,
duration: 2000,
useNativeDriver: true,
})
).start();
}, []);
const spin = rotateAnim.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg'],
});
return (
<Animated.View
style={[
styles.box,
{ transform: [{ rotate: spin }] }
]}
>
<Text>🔄</Text>
</Animated.View>
);
}
const styles = StyleSheet.create({
box: {
width: 100,
height: 100,
backgroundColor: '#007AFF',
justifyContent: 'center',
alignItems: 'center',
borderRadius: 8,
},
});
Kleur Veranderen (zonder Native Driver)
const colorAnim = useRef(new Animated.Value(0)).current;
const backgroundColor = colorAnim.interpolate({
inputRange: [0, 1],
outputRange: ['rgb(0, 122, 255)', 'rgb(255, 59, 48)'],
});
<Animated.View style={{ backgroundColor }}>
<Text>Kleur verandert!</Text>
</Animated.View>
Meerdere Transforms
const anim = useRef(new Animated.Value(0)).current;
const translateX = anim.interpolate({
inputRange: [0, 1],
outputRange: [0, 100],
});
const scale = anim.interpolate({
inputRange: [0, 1],
outputRange: [1, 1.5],
});
<Animated.View
style={{
transform: [
{ translateX },
{ scale },
],
}}
/>
Sequence & Parallel
Combineer meerdere animaties die na elkaar of tegelijk lopen.
Sequence - Na Elkaar
import { useRef } from 'react';
import { Animated, Pressable, Text, StyleSheet } from 'react-native';
export default function SequenceExample() {
const anim = useRef(new Animated.Value(0)).current;
const runSequence = () => {
anim.setValue(0);
Animated.sequence([
// Eerst fade in
Animated.timing(anim, {
toValue: 1,
duration: 500,
useNativeDriver: true,
}),
// Dan wachten
Animated.delay(500),
// Dan fade out
Animated.timing(anim, {
toValue: 0,
duration: 500,
useNativeDriver: true,
}),
]).start();
};
return (
<Pressable onPress={runSequence}>
<Animated.View style={[styles.box, { opacity: anim }]}>
<Text>Sequence!</Text>
</Animated.View>
</Pressable>
);
}
Parallel - Tegelijk
import { useRef } from 'react';
import { Animated, Pressable, StyleSheet } from 'react-native';
export default function ParallelExample() {
const fadeAnim = useRef(new Animated.Value(0)).current;
const slideAnim = useRef(new Animated.Value(-100)).current;
const runParallel = () => {
fadeAnim.setValue(0);
slideAnim.setValue(-100);
Animated.parallel([
Animated.timing(fadeAnim, {
toValue: 1,
duration: 1000,
useNativeDriver: true,
}),
Animated.timing(slideAnim, {
toValue: 0,
duration: 1000,
useNativeDriver: true,
}),
]).start();
};
return (
<Pressable onPress={runParallel}>
<Animated.View
style={[
styles.box,
{
opacity: fadeAnim,
transform: [{ translateX: slideAnim }],
},
]}
>
<Text>Parallel!</Text>
</Animated.View>
</Pressable>
);
}
Loop - Oneindig Herhalen
useEffect(() => {
Animated.loop(
Animated.sequence([
Animated.timing(anim, {
toValue: 1,
duration: 1000,
useNativeDriver: true,
}),
Animated.timing(anim, {
toValue: 0,
duration: 1000,
useNativeDriver: true,
}),
])
).start();
}, []);
Praktijkvoorbeelden
Loading Spinner
import { useRef, useEffect } from 'react';
import { Animated, View, StyleSheet } from 'react-native';
export default function LoadingSpinner() {
const spinAnim = useRef(new Animated.Value(0)).current;
useEffect(() => {
Animated.loop(
Animated.timing(spinAnim, {
toValue: 1,
duration: 1000,
useNativeDriver: true,
})
).start();
}, []);
const spin = spinAnim.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg'],
});
return (
<View style={styles.container}>
<Animated.View
style={[
styles.spinner,
{ transform: [{ rotate: spin }] }
]}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
spinner: {
width: 50,
height: 50,
borderRadius: 25,
borderWidth: 5,
borderColor: '#007AFF',
borderTopColor: 'transparent',
},
});
Pulse Animatie
import { useRef, useEffect } from 'react';
import { Animated, View, StyleSheet } from 'react-native';
export default function PulseAnimation() {
const pulseAnim = useRef(new Animated.Value(1)).current;
useEffect(() => {
Animated.loop(
Animated.sequence([
Animated.timing(pulseAnim, {
toValue: 1.2,
duration: 800,
useNativeDriver: true,
}),
Animated.timing(pulseAnim, {
toValue: 1,
duration: 800,
useNativeDriver: true,
}),
])
).start();
}, []);
return (
<Animated.View
style={[
styles.circle,
{ transform: [{ scale: pulseAnim }] }
]}
/>
);
}
const styles = StyleSheet.create({
circle: {
width: 100,
height: 100,
borderRadius: 50,
backgroundColor: '#007AFF',
},
});
Shake Animatie (Fout)
const shakeAnim = useRef(new Animated.Value(0)).current;
const shake = () => {
Animated.sequence([
Animated.timing(shakeAnim, { toValue: 10, duration: 50, useNativeDriver: true }),
Animated.timing(shakeAnim, { toValue: -10, duration: 50, useNativeDriver: true }),
Animated.timing(shakeAnim, { toValue: 10, duration: 50, useNativeDriver: true }),
Animated.timing(shakeAnim, { toValue: 0, duration: 50, useNativeDriver: true }),
]).start();
};
<Animated.View
style={{
transform: [{ translateX: shakeAnim }]
}}
>
<TextInput style={styles.input} />
</Animated.View>
Samenvatting
Animated.Value
Start van elke animatie
Animated.timing()
Lineaire animatie
Animated.spring()
Verende animatie
interpolate()
Waarden omzetten
Je hebt nu geleerd:
- Animated.Value gebruiken voor animaties
- Timing en Spring animaties maken
- Interpolate voor rotatie en kleur
- Sequence en Parallel voor combinaties
- Loop voor oneindige animaties
- Praktische voorbeelden: spinner, pulse, shake