How to create spinner in React Native with Renanimated 2

Creating spinner animation with reanimated. it shows how to use withRepeat and withTiming

How to create spinner in React Native with Renanimated 2

Reanimated is a React Native library for creating animations and interactions which runs on the UI thread. Reanimated uses Turbo Modules which is part of re-architecture of NativeModules.

Before start coding lets learn some fundamentals  of  Reanimated 2

useSharedValue :  Reanimated 2 run animations in a separate thread.  Shared values help to share these values among threads.

useAnimatedStyle :  This hook is really powerful part of new API of Reanimated 2. When  we use this hook. if any shared value update inside this hooks. useAnimatedStyle react this and update the style and view.

Now lets focus how we create spinner animation with Reanimated 2

First of all, we imported all the necessary things.

import React, { useEffect } from 'react';

import { Text, View, StyleSheet } from 'react-native';
import Animated, {
  useAnimatedStyle,
  useSharedValue,
  withRepeat,
  withTiming,
  cancelAnimation,
  Easing,
} from 'react-native-reanimated';

Then we have to create shared value and animated style. as you can see rotation.value is a dependency. it means whenever value changed it will react to this and update the style.

 const rotation = useSharedValue(0);
 
 
  const animatedStyles = useAnimatedStyle(() => {
    return {
      transform: [
        {
          rotateZ: `${rotation.value}deg`,
        },
      ],
    };
  }, [rotation.value]);

But also we need one more effect to run immediately our animation.

 useEffect(() => {
    rotation.value = withRepeat(
      withTiming(360, {
        duration: 1000,
        easing: Easing.linear,
      }),
      -1
    );
    return () => cancelAnimation(rotation);
  }, []);

rotation.value is updated withTiming animation. But we want this animation to loop forever. for this animation, we need to wrap withRepeat animation. withRepeat takes numberOfReps as a second parameter. The first one is an animation. if the value is negative animations will repeat forever.

Lastly, we need to create Spinner a view, our spinner basically circle with one side different color. if we animate the view, we will see spinning animation.

 return (
    <View style={styles.container}>
      <Animated.View style={[styles.spinner, animatedStyles]} />
    </View>
  );
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    height: '100%',
  },
  spinner: {
    height: 60,
    width: 60,
    borderRadius: 30,
    borderWidth: 7,
    borderTopColor: '#f5f5f5',
    borderRightColor: '#f5f5f5',
    borderBottomColor: '#f5f5f5',
    borderLeftColor: 'green',
  },
});

Complete Code

import React, { useEffect } from 'react';

import { Text, View, StyleSheet } from 'react-native';
import Animated, {
  useAnimatedStyle,
  useSharedValue,
  withRepeat,
  withTiming,
  cancelAnimation,
  Easing,
} from 'react-native-reanimated';

const App = () => {
  const rotation = useSharedValue(0);
  const animatedStyles = useAnimatedStyle(() => {
    return {
      transform: [
        {
          rotateZ: `${rotation.value}deg`,
        },
      ],
    };
  }, [rotation.value]);

  useEffect(() => {
    rotation.value = withRepeat(
      withTiming(360, {
        duration: 1000,
        easing: Easing.linear,
      }),
      200
    );
    return () => cancelAnimation(rotation);
  }, []);

  return (
    <View style={styles.container}>
      <Animated.View style={[styles.spinner, animatedStyles]} />
    </View>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    height: '100%',
  },
  spinner: {
    height: 60,
    width: 60,
    borderRadius: 30,
    borderWidth: 7,
    borderTopColor: '#f5f5f5',
    borderRightColor: '#f5f5f5',
    borderBottomColor: '#f5f5f5',
    borderLeftColor: 'green',
  },
});
export default App;