Search code examples
javascriptreact-nativeanimationsvgreact-native-svg

How to animate a part of an svg file in React Native


I'm working on a react native app, and I need to animate (rotate around central point) a part of an svg file. how can I access the component I want to animate ?

I've tried to convert the svg file in jsx format. but still I cannot access the component I want to rotate

App.js :

import React from 'react';
import { StyleSheet, Text, View, Animated } from 'react-native';
import SvgComponent from './assets/hotFan';


class App extends React.Component {
  constructor(props){
    super(props);
    this.animation = new Animated.Value(0);
  }

  render(){
    const rotation = this.animation.interpolate({
      inputRange: [0, 1],
      outputRange: ['0deg', '360deg']
    });

    return (
      <Animated.View style={{transform: [{rotate: rotation}]}}>
          <SvgComponent/>
      </Animated.View>
    );
  }

  componentDidMount() {

    Animated.loop(
      Animated.timing(this.animation, {toValue: 1, duration: 2000})
    ).start();    
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

export default App;

I cannot put the whole component code here as it surpasses the characters limit. but you can convert https://drive.google.com/file/d/1lKaSWTO1_0cQQN6rtdaw_yWE9qa5nSjV/view?usp=sharing to jsx file using https://www.smooth-code.com/open-source/svgr/playground/

the actual code rotates the whole component around, but the internal arrow is the one that is supposed to rotate instead of the whole component.


Solution

  • You can split the SVG into two components: arrow component, which is the static part of the SVG and a Fan component - the animated part. Then just wrap the Fan component with Animated.View and pass you animation:

     <View>
         <SVG />
         <Animated.View style={animatedStyle}>
            <Fan />
         </Animated.View>
     </View>
    

    The animated component will be positioned "absolute" in the wrapped and will render the animation properties, too:

    const interpolateRotation = this.animatedValue.interpolate({
        inputRange: [0, 1],
        outputRange: ['360deg', '0deg'], 
    });
    
    const animatedStyle = {
        position: 'absolute',
        top: 0,
        left: 0,
        transform: [
            { rotate: interpolateRotation }
        ]
    }
    

    Finally, the easiest part is the prepare the animation and to start it:

    animatedValue = new Animated.Value(1);
    
    componentDidMount() {
        this.startAnimation();
    }
    
    startAnimation = () => {
        this.animatedValue.setValue(0);
        Animated.timing(this.animatedValue, {
          toValue: 1,
          duration: 1500,
          easing: Easing.linear,
        }).start(() => this.startAnimation())
    }
    

    I have created a working demo here.