Leem.fin
Leem.fin

Reputation: 42602

Try to make my SVG element rotating using 'Animated' but it doesn't work

In my react-native project, I am trying to have my spinner rotating. My spinner is a SVG component <Spinner />. It is like this:

import * as React from 'react';
import Svg, {Path} from 'react-native-svg';

function Spinner(props) {
  return (
    <Svg width={24} height={24} fill="none" {...props}>
      <Path
         ...
      />
    </Svg>
  );
}

export default Spinner;

Since it is a static SVG element, my plan is to create a component that can have rotating animation & apply it to any screens which needs a loading spinner. So I firstly created a LoadingSpinner component which is supposed to have the rotation animation with the <Spinner />:

import React, {Component, useState, useEffect} from 'react';
import {View, Animated, Easing} from 'react-native';
import Spinner from './Spinner';

const LoadingSpinner = () => {
  const [spinAnim, setSpinAnim] = useState(new Animated.Value(0));

  useEffect(() => {
    Animated.loop(
      Animated.timing(spinAnim, {
        toValue: 1,
        duration: 3000,
        easing: Easing.linear,
        useNativeDriver: true,
      }),
    ).start();
  });

  return (
    <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
      <Animated.View>
        <Spinner />
      </Animated.View>
    </View>
  );
};

export default LoadingSpinner;

Then, in my screen that needs a rotating spinner I just render the LoadingSpinner,

import LoadingSpinner from '../component/LoadingSpinner'
...

const MyScreen = () => {
     ...
    return (
      <View>
         {status==='loading' ? <LoadingSpinner /> : null}
      </View>
    )

}

When I run my app, I can see the spinner is rendered on the screen but it is not rotating. What am I missing?

Upvotes: 4

Views: 2680

Answers (1)

mcernak
mcernak

Reputation: 9130

You are correctly calculating the value of spinAnim (i.e. it gradually changes from 0 to 1), but you are not using that value anywhere. You need to connect that calculated value to some style to see the results.

In your case, since you're working with rotation, you want the values to go from 0deg to 360deg instead of from 0 to 1. That can be accomplished using the interpolate function.

Once you have the rotation value in degrees, you can construct a style object, which will apply that value as transform.rotate (see animatedStyle in code below).

Finally, you assign that style object to the <Animated.View> to connect it all:

import React, {Component, useState, useEffect} from 'react';
import {View, Animated, Easing} from 'react-native';
import Spinner from './Spinner';

const LoadingSpinner = () => {
  const [spinAnim, setSpinAnim] = useState(new Animated.Value(0));

  const interpolateRotation = spinAnim.interpolate({
              inputRange: [0, 1],
              outputRange: ['0deg', '360deg']
            });
            
  const animatedStyle = {
        transform: [
          { rotate: interpolateRotation }
        ]
  }

  useEffect(() => {        
    Animated.loop(
      Animated.timing(spinAnim, {
        toValue: 1,
        duration: 3000,
        easing: Easing.linear,
      })
    ).start();
  });

  return (
    <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
      <Animated.View style={animatedStyle}>
        <Spinner />
      </Animated.View>
    </View>
  );
};

export default LoadingSpinner;

If the object you want to rotate is not centered in the <Animated.View>, you can change the center of rotation by moving the object, rotating it and then moving it back, e.g.:

const animatedStyle = {
        transform: [
          { translateX: -50},
          { translateY: -50},
          { rotate: interpolateRotation },
          { translateX: 50},
          { translateY: 50},
        ],
  }

A quick demo of the difference that it makes: rotated around center vs rotated around a different point.

Upvotes: 3

Related Questions