React Native has transformed the landscape of mobile app development, enabling developers to create cross-platform applications with ease. Now, with the introduction of Reanimated 2, we're witnessing a quantum leap in animation capabilities. In this comprehensive guide, we'll explore the power of Reanimated 2 and learn how to create fluid, high-performance animations that will take your React Native apps to the next level.
Reanimated 2 is not just an update; it's a complete overhaul of the original library. Designed with performance and developer experience in mind, it introduces a new declarative API and powerful concepts like worklets. These improvements allow for the creation of complex animations that run smoothly on the native thread, minimizing the overhead on the JavaScript thread.
The new API in Reanimated 2 makes it easier than ever to create and manage animations. It provides a more intuitive way to define animated values and styles.
Worklets are small functions that can run on the UI thread, enabling smooth 60 FPS animations even during heavy JavaScript thread activity.
By running animations on the native thread, Reanimated 2 significantly reduces JavaScript bridge traffic, resulting in smoother animations and better overall app performance.
Integration with react-native-gesture-handler has been enhanced, allowing for more responsive and natural-feeling gesture-based animations.
To begin using Reanimated 2 in your React Native project, you'll need to install it first:
npm install react-native-reanimated@next
For iOS development, don't forget to run:
cd ios && pod install
You'll also need to make some changes to your babel.config.js
file:
module.exports = {
presets: ["module:metro-react-native-babel-preset"],
plugins: ["react-native-reanimated/plugin"],
}
Let's start with a simple animation to demonstrate the new API:
import React from "react"
import { Button, StyleSheet } from "react-native"
import Animated, {
useSharedValue,
useAnimatedStyle,
withSpring,
} from "react-native-reanimated"
function AnimatedBox() {
const offset = useSharedValue(0)
const animatedStyles = useAnimatedStyle(() => {
return {
transform: [{ translateX: offset.value }],
}
})
const handlePress = () => {
offset.value = withSpring(Math.random() * 255)
}
return (
<>
<Animated.View style={[styles.box, animatedStyles]} />
<Button onPress={handlePress} title="Move" />
</>
)
}
const styles = StyleSheet.create({
box: {
width: 100,
height: 100,
backgroundColor: "blue",
borderRadius: 20,
},
})
export default AnimatedBox
This code creates a blue box that moves to a random position along the X-axis when you press a button. The animation uses a spring for a natural, bouncy feel.
IMAGE Here
One of the most powerful features of Reanimated 2 is its ability to handle gestures smoothly using worklets. Here's an example of a draggable box:
import React from "react"
import { StyleSheet } from "react-native"
import Animated, {
useAnimatedGestureHandler,
useAnimatedStyle,
useSharedValue,
withSpring,
} from "react-native-reanimated"
import { PanGestureHandler } from "react-native-gesture-handler"
function DraggableBox() {
const x = useSharedValue(0)
const y = useSharedValue(0)
const gestureHandler = useAnimatedGestureHandler({
onStart: (_, ctx) => {
ctx.startX = x.value
ctx.startY = y.value
},
onActive: (event, ctx) => {
x.value = ctx.startX + event.translationX
y.value = ctx.startY + event.translationY
},
onEnd: () => {
x.value = withSpring(0)
y.value = withSpring(0)
},
})
const animatedStyles = useAnimatedStyle(() => {
return {
transform: [{ translateX: x.value }, { translateY: y.value }],
}
})
return (
<PanGestureHandler onGestureEvent={gestureHandler}>
<Animated.View style={[styles.box, animatedStyles]} />
</PanGestureHandler>
)
}
const styles = StyleSheet.create({
box: {
width: 100,
height: 100,
backgroundColor: "violet",
borderRadius: 20,
},
})
export default DraggableBox
This code creates a violet box that you can drag around the screen. When you release it, it springs back to its original position.
IMAGE Here
Reanimated 2 provides powerful tools for creating complex animations through interpolations and derived values. Here's an example of a color-changing box based on its position:
import React from "react"
import { Dimensions, StyleSheet } from "react-native"
import Animated, {
useAnimatedGestureHandler,
useAnimatedStyle,
useSharedValue,
interpolateColor,
} from "react-native-reanimated"
import { PanGestureHandler } from "react-native-gesture-handler"
const { width } = Dimensions.get("window")
function ColorChangingBox() {
const x = useSharedValue(0)
const gestureHandler = useAnimatedGestureHandler({
onActive: (event) => {
x.value = event.translationX
},
onEnd: () => {
x.value = 0
},
})
const animatedStyles = useAnimatedStyle(() => {
const backgroundColor = interpolateColor(
x.value,
[-width / 2, 0, width / 2],
["#ff0000", "#00ff00", "#0000ff"]
)
return {
backgroundColor,
transform: [{ translateX: x.value }],
}
})
return (
<PanGestureHandler onGestureEvent={gestureHandler}>
<Animated.View style={[styles.box, animatedStyles]} />
</PanGestureHandler>
)
}
const styles = StyleSheet.create({
box: {
width: 100,
height: 100,
borderRadius: 20,
},
})
export default ColorChangingBox
This example creates a box that changes color as you drag it horizontally across the screen.
IMAGE Here
Reanimated 2 is designed with performance in mind, but there are still best practices to follow:
useSharedValue
for values that need to be animated or changed over time.useAnimatedStyle
efficiently.useDerivedValue
for values that depend on other animated values.Here's an example of using useDerivedValue
for efficient animations:
import Animated, {
useSharedValue,
useDerivedValue,
useAnimatedStyle,
withTiming,
} from "react-native-reanimated"
function EfficiencyExample() {
const progress = useSharedValue(0)
const scale = useDerivedValue(() => 1 + progress.value * 0.5)
const rotate = useDerivedValue(() => `${progress.value * 2 * Math.PI}rad`)
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ scale: scale.value }, { rotate: rotate.value }],
}))
// ... rest of the component
}
Let's look at a more complex, real-world example: a scrollable carousel with parallax effect:
import React from "react"
import { Dimensions, StyleSheet, View } from "react-native"
import Animated, {
useSharedValue,
useAnimatedScrollHandler,
useAnimatedStyle,
interpolate,
Extrapolate,
} from "react-native-reanimated"
const { width: PAGE_WIDTH } = Dimensions.get("window")
const CARD_WIDTH = PAGE_WIDTH * 0.8
const CARD_HEIGHT = CARD_WIDTH * 1.5
const VISIBLE_ITEMS = 3
function ParallaxCarousel() {
const scrollX = useSharedValue(0)
const scrollHandler = useAnimatedScrollHandler({
onScroll: (event) => {
scrollX.value = event.contentOffset.x
},
})
const data = Array.from({ length: 10 }, (_, i) => i)
return (
<Animated.ScrollView
horizontal
pagingEnabled
showsHorizontalScrollIndicator={false}
onScroll={scrollHandler}
scrollEventThrottle={16}
>
{data.map((_, index) => (
<View style={styles.pageContainer} key={index}>
<Card index={index} scrollX={scrollX} />
</View>
))}
</Animated.ScrollView>
)
}
function Card({ index, scrollX }) {
const animatedStyle = useAnimatedStyle(() => {
const inputRange = [
(index - 1) * PAGE_WIDTH,
index * PAGE_WIDTH,
(index + 1) * PAGE_WIDTH,
]
const translateY = interpolate(
scrollX.value,
inputRange,
[50, 0, 50],
Extrapolate.CLAMP
)
const scale = interpolate(
scrollX.value,
inputRange,
[0.8, 1, 0.8],
Extrapolate.CLAMP
)
return {
transform: [{ translateY }, { scale }],
}
})
return <Animated.View style={[styles.card, animatedStyle]} />
}
const styles = StyleSheet.create({
pageContainer: {
width: PAGE_WIDTH,
alignItems: "center",
justifyContent: "center",
},
card: {
width: CARD_WIDTH,
height: CARD_HEIGHT,
backgroundColor: "tomato",
borderRadius: 16,
},
})
export default ParallaxCarousel
This example creates a horizontal scrollable carousel where the cards scale and translate based on their position, creating a parallax effect.
IMAGE Here
When working with Reanimated 2, you might encounter some common issues:
react-native-gesture-handler
and are using it correctly with Reanimated 2.useSharedValue
and useAnimatedStyle
. Ensure you're updating shared values correctly.babel.config.js
and linked the library correctly.React Native Reanimated 2 is a game-changer for creating high-performance animations in React Native applications. Its new declarative API, combined with the power of worklets, opens up a world of possibilities for creating smooth, complex animations that run at 60 FPS, even on lower-end devices.
As you continue to explore Reanimated 2, you'll discover even more features and capabilities. The key to mastering this library is practice and experimentation. Don't be afraid to push the boundaries and create animations that were previously difficult or impossible with React Native.
Remember, great animations can significantly enhance the user experience of your app. With Reanimated 2, you have the tools to create these experiences efficiently and effectively.
Happy animating! 🤖