Animated smartphone UI with flowing shapes and code, observed by a British shorthair cat

Unleashing Motion Magic with React Native Reanimated

The Gray Cat
The Gray Cat

React Native Reanimated is a powerful library that reimagines animation capabilities in React Native applications. It provides a more comprehensive, low-level abstraction for the Animated library API, allowing developers to create fluid, high-performance animations and gesture-driven interfaces. With Reanimated, you can bring your mobile apps to life with smooth transitions, interactive elements, and complex UI behaviors that were previously challenging to implement.

Key Features of React Native Reanimated

React Native Reanimated offers a rich set of features that set it apart from traditional animation libraries:

  • Native Driver: Utilizes native-driven animations for optimal performance.
  • Worklets: Allows running JavaScript on the UI thread for smoother animations.
  • Gesture Handling: Seamlessly integrates with gesture libraries for interactive animations.
  • Shared Values: Provides a way to share values between the JavaScript thread and the UI thread.
  • Layout Animations: Enables easy creation of layout transitions and animations.

Getting Started with Reanimated

To begin your journey with React Native Reanimated, you’ll first need to install the library in your project. Open your terminal and run:

npm install react-native-reanimated

For yarn users:

yarn add react-native-reanimated

After installation, you’ll need to configure your project to use Reanimated. This process may vary depending on your React Native version and whether you’re using Expo or bare React Native.

Basic Usage: Creating Your First Animation

Let’s start with a simple example to demonstrate the power of Reanimated. We’ll create a box that fades in when the component mounts.

import React from 'react';
import { View } from 'react-native';
import Animated, { useSharedValue, useAnimatedStyle, withTiming } from 'react-native-reanimated';

const FadingBox = () => {
  const opacity = useSharedValue(0);

  const animatedStyle = useAnimatedStyle(() => {
    return {
      opacity: opacity.value,
    };
  });

  React.useEffect(() => {
    opacity.value = withTiming(1, { duration: 1000 });
  }, []);

  return (
    <Animated.View
      style={[
        { width: 100, height: 100, backgroundColor: 'blue' },
        animatedStyle,
      ]}
    />
  );
};

In this example, we use useSharedValue to create a mutable value that can be shared between the JavaScript thread and the UI thread. The useAnimatedStyle hook creates a style object that depends on this shared value. Finally, withTiming is used to animate the opacity from 0 to 1 over a duration of 1000 milliseconds.

Advanced Usage: Gesture-Driven Animations

One of Reanimated’s strengths is its ability to create smooth, gesture-driven animations. Let’s create a draggable box that springs back to its original position when released.

import React from 'react';
import { View, StyleSheet } from 'react-native';
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  useAnimatedGestureHandler,
  withSpring,
} from 'react-native-reanimated';
import { PanGestureHandler } from 'react-native-gesture-handler';

const DraggableBox = () => {
  const translateX = useSharedValue(0);
  const translateY = useSharedValue(0);

  const panGestureHandler = useAnimatedGestureHandler({
    onStart: (_, ctx) => {
      ctx.startX = translateX.value;
      ctx.startY = translateY.value;
    },
    onActive: (event, ctx) => {
      translateX.value = ctx.startX + event.translationX;
      translateY.value = ctx.startY + event.translationY;
    },
    onEnd: () => {
      translateX.value = withSpring(0);
      translateY.value = withSpring(0);
    },
  });

  const animatedStyle = useAnimatedStyle(() => {
    return {
      transform: [
        { translateX: translateX.value },
        { translateY: translateY.value },
      ],
    };
  });

  return (
    <PanGestureHandler onGestureEvent={panGestureHandler}>
      <Animated.View style={[styles.box, animatedStyle]} />
    </PanGestureHandler>
  );
};

const styles = StyleSheet.create({
  box: {
    width: 100,
    height: 100,
    backgroundColor: 'blue',
  },
});

This example demonstrates how to use useAnimatedGestureHandler to create a gesture handler that updates shared values based on the user’s touch input. The withSpring function is used to create a spring animation that returns the box to its original position when the gesture ends.

Worklets: Running JavaScript on the UI Thread

Worklets are a unique feature of Reanimated that allow you to run JavaScript code on the UI thread, ensuring smooth animations even when the JavaScript thread is busy. Here’s an example of how to use a worklet to create a custom animation:

import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withRepeat,
  withTiming,
  Easing,
  runOnUI,
} from 'react-native-reanimated';

const PulsingCircle = () => {
  const scale = useSharedValue(1);

  const pulseWorklet = runOnUI((amplitude: number, duration: number) => {
    'worklet';
    scale.value = withRepeat(
      withTiming(amplitude, { duration, easing: Easing.inOut(Easing.ease) }),
      -1,
      true
    );
  });

  React.useEffect(() => {
    pulseWorklet(1.2, 1000);
  }, []);

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ scale: scale.value }],
  }));

  return <Animated.View style={[styles.circle, animatedStyle]} />;
};

In this example, we define a worklet using runOnUI that creates a pulsing animation. The worklet is executed on the UI thread, ensuring smooth performance even if the JavaScript thread becomes blocked.

Layout Animations: Smooth Transitions

Reanimated also provides powerful tools for creating layout animations. Here’s an example of how to use Layout.springify() to create smooth transitions when elements are added or removed from a list:

import Animated, { Layout } from 'react-native-reanimated';

const AnimatedList = ({ items }) => {
  return (
    <Animated.FlatList
      data={items}
      renderItem={({ item }) => (
        <Animated.View
          layout={Layout.springify()}
          style={styles.listItem}
        >
          <Text>{item}</Text>
        </Animated.View>
      )}
      keyExtractor={(item) => item}
    />
  );
};

This code creates a list where items smoothly animate into place when added or removed, using a spring animation for a natural feel.

Conclusion

React Native Reanimated opens up a world of possibilities for creating fluid, high-performance animations in your mobile applications. From simple fades to complex gesture-driven interfaces, Reanimated provides the tools you need to bring your UI to life. By leveraging features like worklets, shared values, and native drivers, you can create animations that run smoothly even on less powerful devices.

As you continue to explore Reanimated, you’ll discover even more advanced techniques and optimizations. For further reading, check out our articles on React Native Gesture Handler and React Native Skia to learn how to combine these libraries for even more powerful UI interactions.

Remember, great animations are about enhancing user experience, not just adding visual flair. Use Reanimated thoughtfully to guide users through your app, provide feedback, and create delightful interactions that keep them coming back for more.

Comments