React Native Animation Basics
Importance and Core Concepts of Animations
Animations enhance the interactivity of applications, for example:
- Visual Feedback: Scaling effects on button presses.
- Page Transitions: Sliding animations during screen changes.
- User Guidance: Highlighting new features.
React Native’s animation system is based on a declarative API, where values are bound to animation variables and driven by time or gestures.
Supported Animation Types
- Property Animations: Modify properties like
opacityortransform. - Position Animations: Change a component’s position.
- Gesture Animations: Respond to user touches or drags.
Animated API
Core Components of Animated API
Animated is React Native’s built-in animation library. Its main components include:
Animated.Value: Animation value that drives changes.Animated.timing: Time-driven linear animations.Animated.spring: Spring animations that simulate physical effects.Animated.sequence/parallel: Combine multiple animations.
Basic Animation Implementation (Fade In/Out)
We’ll implement a simple fade-in effect.
Example Code
import React, { useRef, useEffect } from 'react';
import { View, Animated, Button, StyleSheet } from 'react-native';
const App = () => {
const fadeAnim = useRef(new Animated.Value(0)).current;
useEffect(() => {
Animated.timing(fadeAnim, {
toValue: 1,
duration: 2000,
useNativeDriver: true,
}).start();
}, [fadeAnim]);
const resetAnimation = () => {
fadeAnim.setValue(0);
Animated.timing(fadeAnim, {
toValue: 1,
duration: 2000,
useNativeDriver: true,
}).start();
};
return (
<View style={styles.container}>
<Animated.View style={[styles.box, { opacity: fadeAnim }]} />
<Button title="Reset" onPress={resetAnimation} />
</View>
);
};
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
box: { width: 100, height: 100, backgroundColor: 'tomato' },
});
export default App;
Analysis
Animated.Value: Initialized to 0, representing full transparency.Animated.timing: Transitions the value to 1 (fully opaque) over 2 seconds.useNativeDriver: Uses native driver for better performance.- Reset: Uses
setValueto reset the animation value.
Composite and Sequential Animations
We can combine multiple animations, such as scaling and moving simultaneously.
Example Code
import React, { useRef } from 'react';
import { View, Animated, Button, StyleSheet } from 'react-native';
const App = () => {
const scaleAnim = useRef(new Animated.Value(1)).current;
const translateAnim = useRef(new Animated.Value(0)).current;
const runAnimation = () => {
Animated.parallel([
Animated.timing(scaleAnim, {
toValue: 1.5,
duration: 1000,
useNativeDriver: true,
}),
Animated.timing(translateAnim, {
toValue: 100,
duration: 1000,
useNativeDriver: true,
}),
]).start(() => {
Animated.sequence([
Animated.timing(scaleAnim, { toValue: 1, duration: 500, useNativeDriver: true }),
Animated.timing(translateAnim, { toValue: 0, duration: 500, useNativeDriver: true }),
]).start();
});
};
return (
<View style={styles.container}>
<Animated.View
style={[
styles.box,
{ transform: [{ scale: scaleAnim }, { translateY: translateAnim }] },
]}
/>
<Button title="Run Animation" onPress={runAnimation} />
</View>
);
};
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
box: { width: 100, height: 100, backgroundColor: 'purple' },
});
export default App;
Analysis
Animated.parallel: Executes scaling and translation simultaneously.Animated.sequence: Sequentially executes scale-down and reset.transform: Supports multiple transformations (e.g.,scale,translateY).
Gesture-Driven Animations
Use PanResponder to implement a draggable animation.
Example Code
import React, { useRef } from 'react';
import { View, Animated, PanResponder, StyleSheet } from 'react-native';
const App = () => {
const pan = useRef(new Animated.ValueXY()).current;
const panResponder = PanResponder.create({
onMoveShouldSetPanResponder: () => true,
onPanResponderMove: Animated.event([null, { dx: pan.x, dy: pan.y }], {
useNativeDriver: false,
}),
onPanResponderRelease: () => {
Animated.spring(pan, {
toValue: { x: 0, y: 0 },
useNativeDriver: true,
}).start();
},
});
return (
<View style={styles.container}>
<Animated.View
style={[styles.box, { transform: [{ translateX: pan.x }, { translateY: pan.y }] }]}
{...panResponder.panHandlers}
/>
</View>
);
};
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
box: { width: 100, height: 100, backgroundColor: 'teal' },
});
export default App;
Analysis
PanResponder: Handles touch events.Animated.event: Binds gesture offsets to animation values.Animated.spring: Snaps back to the original position on release.
React-Native-Reanimated
Advantages and Installation of Reanimated
react-native-reanimated (Reanimated) is a community-developed animation library with advantages including:
- Native Performance: Animation logic runs on the UI thread.
- Flexibility: Supports complex gestures and animations.
- Worklets: Allows defining animation logic on the native thread.
Installation
npm install react-native-reanimated
Add the plugin to babel.config.js:
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: ['react-native-reanimated/plugin'],
};
Basic Animation Implementation
Implement a fade-in animation.
Example Code
import React from 'react';
import { View, Button, StyleSheet } from 'react-native';
import Animated, {
useSharedValue,
useAnimatedStyle,
withTiming,
} from 'react-native-reanimated';
const App = () => {
const opacity = useSharedValue(0);
const animatedStyle = useAnimatedStyle(() => ({
opacity: opacity.value,
}));
const startAnimation = () => {
opacity.value = withTiming(1, { duration: 2000 });
};
return (
<View style={styles.container}>
<Animated.View style={[styles.box, animatedStyle]} />
<Button title="Start" onPress={startAnimation} />
</View>
);
};
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
box: { width: 100, height: 100, backgroundColor: 'coral' },
});
export default App;
Analysis
useSharedValue: Shared animation value.useAnimatedStyle: Dynamically generates styles.withTiming: Time-driven animation.
Optimizing Performance with Worklets
Worklets allow custom logic to run on the UI thread.
Example Code
import React from 'react';
import { View, Button, StyleSheet } from 'react-native';
import Animated, {
useSharedValue,
useAnimatedStyle,
withRepeat,
withTiming,
} from 'react-native-reanimated';
const App = () => {
const scale = useSharedValue(1);
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ scale: scale.value }],
}));
const startAnimation = () => {
scale.value = withRepeat(
withTiming(1.5, { duration: 1000 }),
-1, // Infinite repeat
true // Reverse playback
);
};
return (
<View style={styles.container}>
<Animated.View style={[styles.box, animatedStyle]} />
<Button title="Pulse" onPress={startAnimation} />
</View>
);
};
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
box: { width: 100, height: 100, backgroundColor: 'lime' },
});
export default App;
Analysis
withRepeat: Repeats the animation.- Worklet: Logic in
useAnimatedStyleruns as a Worklet on the UI thread.
Gesture Animations with Reanimated
Combine react-native-gesture-handler for drag animations.
Installation
npm install react-native-gesture-handler
Example Code
import React from 'react';
import { View, StyleSheet } from 'react-native';
import Animated, {
useSharedValue,
useAnimatedStyle,
withSpring,
} from 'react-native-reanimated';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
const App = () => {
const offsetX = useSharedValue(0);
const offsetY = useSharedValue(0);
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ translateX: offsetX.value }, { translateY: offsetY.value }],
}));
const panGesture = Gesture.Pan()
.onUpdate(event => {
offsetX.value = event.translationX;
offsetY.value = event.translationY;
})
.onEnd(() => {
offsetX.value = withSpring(0);
offsetY.value = withSpring(0);
});
return (
<View style={styles.container}>
<GestureDetector gesture={panGesture}>
<Animated.View style={[styles.box, animatedStyle]} />
</GestureDetector>
</View>
);
};
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
box: { width: 100, height: 100, backgroundColor: 'indigo' },
});
export default App;
Analysis
Gesture.Pan: Handles drag gestures.onUpdate: Updates position in real-time.withSpring: Snaps back on release.
Comprehensive Case Study
Requirement Analysis
We’ll create an interactive card that:
- Scales and rotates on tap.
- Supports dragging, resetting on release.
- Implements solutions using both Animated API and Reanimated.
Implementation with Animated API and Reanimated
Animated API Implementation
import React, { useRef } from 'react';
import { View, Animated, PanResponder, TouchableOpacity, StyleSheet } from 'react-native';
const App = () => {
const scale = useRef(new Animated.Value(1)).current;
const rotate = useRef(new Animated.Value(0)).current;
const pan = useRef(new Animated.ValueXY()).current;
const panResponder = PanResponder.create({
onMoveShouldSetPanResponder: () => true,
onPanResponderMove: Animated.event([null, { dx: pan.x, dy: pan.y }], {
useNativeDriver: false,
}),
onPanResponderRelease: () => {
Animated.spring(pan, { toValue: { x: 0, y: 0 }, useNativeDriver: true }).start();
},
});
const rotateInterpolate = rotate.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg'],
});
const animateCard = () => {
Animated.parallel([
Animated.timing(scale, { toValue: 1.5, duration: 500, useNativeDriver: true }),
Animated.timing(rotate, { toValue: 1, duration: 500, useNativeDriver: true }),
]).start(() => {
Animated.parallel([
Animated.timing(scale, { toValue: 1, duration: 500, useNativeDriver: true }),
Animated.timing(rotate, { toValue: 0, duration: 500, useNativeDriver: true }),
]).start();
});
};
return (
<View style={styles.container}>
<TouchableOpacity onPress={animateCard}>
<Animated.View
style={[
styles.card,
{
transform: [
{ scale },
{ rotate: rotateInterpolate },
{ translateX: pan.x },
{ translateY: pan.y },
],
},
]}
{...panResponder.panHandlers}
/>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
card: { width: 150, height: 150, backgroundColor: 'salmon' },
});
export default App;
Reanimated Implementation
import React from 'react';
import { View, StyleSheet } from 'react-native';
import Animated, {
useSharedValue,
useAnimatedStyle,
withTiming,
withSpring,
} from 'react-native-reanimated';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
const App = () => {
const scale = useSharedValue(1);
const rotate = useSharedValue(0);
const offsetX = useSharedValue(0);
const offsetY = useSharedValue(0);
const animatedStyle = useAnimatedStyle(() => ({
transform: [
{ scale: scale.value },
{ rotate: `${rotate.value}deg` },
{ translateX: offsetX.value },
{ translateY: offsetY.value },
],
}));
const tapGesture = Gesture.Tap().onEnd(() => {
scale.value = withTiming(1.5, { duration: 500 }, () => {
scale.value = withTiming(1, { duration: 500 });
});
rotate.value = withTiming(360, { duration: 500 }, () => {
rotate.value = withTiming(0, { duration: 500 });
});
});
const panGesture = Gesture.Pan()
.onUpdate(event => {
offsetX.value = event.translationX;
offsetY.value = event.translationY;
})
.onEnd(() => {
offsetX.value = withSpring(0);
offsetY.value = withSpring(0);
});
const composedGesture = Gesture.Exclusive(panGesture, tapGesture);
return (
<View style={styles.container}>
<GestureDetector gesture={composedGesture}>
<Animated.View style={[styles.card, animatedStyle]} />
</GestureDetector>
</View>
);
};
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
card: { width: 150, height: 150, backgroundColor: 'salmon' },
});
export default App;
Analysis
- Animated API: Uses
timingandPanResponderfor animations and dragging. - Reanimated: Leverages
withTimingandGestureAPI for cleaner code and better performance. - Differences: Reanimated runs on the UI thread, avoiding JavaScript thread bottlenecks.
Advanced Techniques and Considerations
Performance Optimization Tips
- Use
useNativeDriver: Enable it in Animated API whenever possible. - Avoid Frequent Updates: Minimize unnecessary animation triggers.
- Worklets: Use Worklets in Reanimated to offload complex logic to the UI thread.
Debugging Animation Techniques
- Log Output: Print animation values (e.g.,
fadeAnim.addListenerorconsole.log). - Slow Animations: Extend
durationto inspect effects. - React Native Debugger: Monitor animation frame rates.



