DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 967,911 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Cover image for How to use Reanimated 2 (a beginners guide)
Spencer Carli
Spencer Carli

Posted on • Originally published at reactnativeschool.com

How to use Reanimated 2 (a beginners guide)

Reanimated 2 is a hyper powerful library that all the cool kids use but has always been a bit of a blackbox to me... I saw the power of it but was overwhelmed by the API.

This post was originally published on React Native School. If you’re interested in hundreds of React Native tutorials and courses be sure to check it out!

Today I want to share with you what I've learned and we'll create our first Reanimated 2 animation together - an image that changes sizes.

Let's start with an app that renders a 100x100 image. All standard React Native.

import React from 'react';
import { View, TouchableOpacity, Image } from 'react-native';

export default () => {
  return (
    <View
      style={{
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
        flexDirection: 'column',
        backgroundColor: '#7CA1B4',
      }}
    >
      <TouchableOpacity
        onPress={() => {
          alert('todo!');
        }}
      >
        <Image
          source={require('./rns.png')}
          resizeMode="contain"
          style={{ width: 100, height: 100 }}
        />
      </TouchableOpacity>
    </View>
  );
};
Enter fullscreen mode Exit fullscreen mode

01

We'll be animating the size of the image. That means that we need to store the value to be animated somewhere.

Reanimated 2 has a concept of shared values. These are values that can be shared across different threads (think JavaScript thread and UI thread) thus the name of shared value.

It's where you'll want to store your "animateable" data - in this case the random number that we'll use to drive the size of the image.

useSharedValue returns a value - similar to using .current on a useRef hook - that means that we can't just access the result directly. We need to access the .value property of the returned data.

Also note in the following code that I've replaced the Image component with an Animated.Image. This is in preparation for when the randomNumber value will change.

import React from 'react';
import { View, TouchableOpacity, Image } from 'react-native';
import Animated, { useSharedValue } from 'react-native-reanimated';

export default () => {
  const randomNumber = useSharedValue(100);

  return (
    <View
      style={{
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
        flexDirection: 'column',
        backgroundColor: '#7CA1B4',
      }}
    >
      <TouchableOpacity
        onPress={() => {
          alert('todo!');
        }}
      >
        <Animated.Image
          source={require('./rns.png')}
          resizeMode="contain"
          style={{ width: randomNumber.value, height: randomNumber.value }}
        />
      </TouchableOpacity>
    </View>
  );
};
Enter fullscreen mode Exit fullscreen mode

Now let's go ahead and change that randomNumber. Make sure you use .value when accessingly the data.

import React from 'react';
import { View, TouchableOpacity, Image } from 'react-native';
import Animated, { useSharedValue } from 'react-native-reanimated';

export default () => {
  const randomNumber = useSharedValue(100);

  return (
    <View
      style={{
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
        flexDirection: 'column',
        backgroundColor: '#7CA1B4',
      }}
    >
      <TouchableOpacity
        onPress={() => {
          randomNumber.value = Math.random() * 350;
        }}
      >
        <Animated.Image
          source={require('./rns.png')}
          resizeMode="contain"
          style={{ width: randomNumber.value, height: randomNumber.value }}
        />
      </TouchableOpacity>
    </View>
  );
};
Enter fullscreen mode Exit fullscreen mode

But when we press the image nothing is happening... why?

It's not changing because, unlike when calling setState, it doesn't cause a re-render to happen (which is good) we're just changing a value.

What we need to do is spawn a worklet that can adjust to changes in randomNumber.

This worklet will run on the UI thread (rather than the JavaScript thread) allowing us to see a smooth native animation.

To spawn this worklet we're going to use the useAnimatedStyle hook. This hook will return a style object that updates with randomNumber. This style object can then be applied to our Animated.Image.

useAnimateStyle is similar to useEffect but actually returns a value (the style object).

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

export default () => {
  const randomNumber = useSharedValue(100);

  const style = useAnimatedStyle(() => {
    return { width: randomNumber.value, height: randomNumber.value };
  });

  return (
    <View
      style={{
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
        flexDirection: 'column',
        backgroundColor: '#7CA1B4',
      }}
    >
      <TouchableOpacity
        onPress={() => {
          randomNumber.value = Math.random() * 350;
        }}
      >
        <Animated.Image
          source={require('./rns.png')}
          resizeMode="contain"
          style={style}
        />
      </TouchableOpacity>
    </View>
  );
};
Enter fullscreen mode Exit fullscreen mode

Now when we press the image the size of it changes (granted it's not animated - we'll get to that).

https://media.giphy.com/media/GjKW16FwHfXOtkadLy/giphy.gif

The final piece of (this) puzzle is to actually animate the values. We've created all the necessary relationships between data and have things updating where they need to be - now we need to choose the animation to use.

Today we'll make "springy".

By importing withSpring we can tell Reanimated 2 how we want that data to change.

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

export default () => {
  const randomNumber = useSharedValue(100);

  const style = useAnimatedStyle(() => {
    return {
      width: randomNumber.value,
      height: randomNumber.value,
    };
  });

  return (
    <View
      style={{
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
        flexDirection: 'column',
        backgroundColor: '#7CA1B4',
      }}
    >
      <TouchableOpacity
        onPress={() => {
          randomNumber.value = withSpring(Math.random() * 350);
        }}
      >
        <Animated.Image
          source={require('./rns.png')}
          resizeMode="contain"
          style={style}
        />
      </TouchableOpacity>
    </View>
  );
};
Enter fullscreen mode Exit fullscreen mode

https://media.giphy.com/media/RSukX8m2ogtoKF0NWO/giphy.gif

Alternatively we could use withSpring inside of useAnimatedStyle to customize how each property changes.

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

export default () => {
  const randomNumber = useSharedValue(100);

  const style = useAnimatedStyle(() => {
    return {
      width: withSpring(randomNumber.value),
      height: withSpring(randomNumber.value, { stiffness: 10 }),
    };
  });

  return (
    <View
      style={{
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
        flexDirection: 'column',
        backgroundColor: '#7CA1B4',
      }}
    >
      <TouchableOpacity
        onPress={() => {
          randomNumber.value = Math.random() * 350;
        }}
      >
        <Animated.Image
          source={require('./rns.png')}
          resizeMode="contain"
          style={style}
        />
      </TouchableOpacity>
    </View>
  );
};
Enter fullscreen mode Exit fullscreen mode

https://media.giphy.com/media/9cJXjCvcDq41WKRvKz/giphy.gif

And that's on me trying to figure out the moving parts of Reanimated 2. To me it's a pretty complex library to first learn but the power of it is incredible.

If you have any comments/corrections/questions please reach out on Twitter.

Top comments (1)

Collapse
 
devoresyah profile image
DeVoresyah ArEst

awesome, thanks for sharing. When I try to learn Reanimated 2 by myself, it took like 1 week for me to understand. But this article really helpful with awesome explanation and easy to understand, really love it <3

🌚 Friends don't let friends browse without dark mode.

Sorry, it's true.