Skip to content

Instantly share code, notes, and snippets.

@levynir
Last active January 19, 2021 09:52
Show Gist options
  • Save levynir/5962de39879a0b8eb1a2fd77ccedb2d8 to your computer and use it in GitHub Desktop.
Save levynir/5962de39879a0b8eb1a2fd77ccedb2d8 to your computer and use it in GitHub Desktop.
React Native Rotating Component
/**
* Created by nirlevy on 02/07/2017.
* MIT Licence
*/
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Animated, Easing } from 'react-native';
export class RotatingView extends Component {
state = {
spinValue: new Animated.Value(0),
}
componentDidMount() {
Animated.timing(
this.state.spinValue, // The animated value to drive
{
toValue: this.props.toValue || 1, // Animate to 360/value
duration: this.props.duration || 2000, // Make it take a while
easing: Easing.linear,
useNativeDriver: true,
}
).start(this.props.onFinishedAnimating); // Starts the animation
}
render() {
let spin = this.state.spinValue.interpolate({
inputRange: [0,1],
outputRange: ['0deg', '360deg']
});
return (
<Animated.View
style={{
...this.props.style,
transform: [{rotate:spin}], // Bind rotation to animated value
}}
>
{this.props.children}
</Animated.View>
);
}
};
RotatingView.propTypes = Object.assign({},Animated.View.propTypes,{
duration: PropTypes.number, //How long
toValue: PropTypes.number, //How much rotation, eg. 0.5 will rotate 180deg
onFinishedAnimating: PropTypes.func, //What to do when animation finishes, see Aminated docs
});
/*
USAGE:
<View style={{flex: 1, flexDirection: 'row', justifyContent: 'center'}}>
<RotatingView
style={{height: 200, width: 200,}}
duration={3000}
onFinishedAnimating={( (status) => {console.log(status)} )}
>
<Image
style={{height:'100%', width: '100%', resizeMode: 'contain'}}
resizeMode='contain'
source={require("image.png")}/>
</RotatingView>
</View>
*/
@AlMel3000
Copy link

Thanks a lot!

@meijingkang
Copy link

how to make it rotate all the time

@andrescheca
Copy link

andrescheca commented Oct 23, 2017

Thanks @levynir!

@meijingkang, not sure if you have solved this already, but in order to have it rotate all the time what I did was to have a property called loop and each time the animation ends it checks that property and call the same function again.

  static propTypes = {
    onFinishedAnimating: PropTypes.func,
    loop: PropTypes.bool,
  };

  static defaultProps = {
    onFinishedAnimating: () => {},
    loop: false,
  };

  spin() {
    this.state.spinValue.setValue(0);
    Animated.timing(this.state.spinValue, {
      toValue: this.props.toValue || 1,
      duration: this.props.duration || 2000,
      easing: Easing.linear,
      useNativeDriver: true,
    }).start(() => {
      if (this.props.loop) {
        this.spin();
      }
      if (this.props.onFinishedAnimating) {
        this.props.onFinishedAnimating();
      }
    });
  }

  componentDidMount() {
    this.spin();
  }

@benallfree
Copy link

benallfree commented Jul 21, 2018

Another loop implementation that handles cases where loop={true} and toValue!=1 (partial rotations):

import React, { Component } from 'react'
import { View, Animated, Easing } from 'react-native'
import PropTypes from 'prop-types'

/*
USAGE:
  <View style={{flex: 1, flexDirection: 'row', justifyContent: 'center'}}>
      <RotatingView
          style={{height: 200, width: 200,}}
          duration={3000}
          onFinishedAnimating={( (status) => {console.log(status)} )}
      >
          <Image
              style={{height:'100%', width: '100%', resizeMode: 'contain'}}
              resizeMode='contain'
              source={require("image.png")}/>
      </RotatingView>
  </View>
*/

export default class RotatingView extends Component {
  static propTypes = Object.assign({}, Animated.View.propTypes, {
    duration: PropTypes.number, //How long
    toValue: PropTypes.number, //How much rotation, eg. 0.5 will rotate 180deg
    onFinishedAnimating: PropTypes.func, //What to do when animation finishes, see Aminated docs
    loop: PropTypes.bool // Should animation loop?
  })

  static defaultProps = {
    duration: 3000,
    onFinishedAnimating: () => {
      console.log('Finished animation')
    },
    loop: false,
    toValue: 1
  }

  state = {
    spinValue: new Animated.Value(0),
    toValue: this.props.toValue
  }

  componentDidMount() {
    this.animate()
  }

  animate() {
    Animated.timing(
      this.state.spinValue, // The animated value to drive
      {
        toValue: this.state.toValue, // Animate to 360/value
        duration: this.props.duration || 2000, // Make it take a while
        easing: Easing.linear,
        useNativeDriver: true
      }
    ).start(this.handleFinishedAnimating) // Starts the animation
  }

  handleFinishedAnimating = () => {
    if (this.props.loop) {
      this.setState(
        {
          spinValue: new Animated.Value(this.state.toValue),
          toValue: this.state.toValue + this.props.toValue
        },
        this.animate
      )
    } else {
      this.props.onFinishedAnimating()
    }
  }

  render() {
    let spin = this.state.spinValue.interpolate({
      inputRange: [0, 1],
      outputRange: ['0deg', '360deg']
    })
    return (
      <Animated.View
        style={{
          ...this.props.style,
          transform: [{ rotate: spin }] // Bind rotation to animated value
        }}
      >
        {this.props.children}
      </Animated.View>
    )
  }
}

@jasonwiener
Copy link

@benallfree that's excellent!

@houman-sanati
Copy link

how to rotate around a specific pivot?

@levynir
Copy link
Author

levynir commented Jul 17, 2019

@Houman7601 I'm not sure actually. sorry.

@michaelzive
Copy link

@levynir: you can rotate around different axis by modifying the 'transform' (in the render() function) to either rotateY or rotateX

  render() {
    let spin = this.state.spinValue.interpolate({
      inputRange: [0, 1],
      outputRange: ["0deg", "360deg"]
    });
    return (
      <Animated.View
        style={{
          ...this.props.style,
          transform: [{ rotateY: spin }] // Bind rotation to animated value
        }}
      >
        {this.props.children}
      </Animated.View>
    );
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment