85

Rotation is a style transform and in RN, you can rotate things like this

  render() {
    return (
      <View style={{transform:[{rotate: '10 deg'}]}}>
        <Image source={require('./logo.png')} />
      </View>
    );
  }

However, to animate things in RN, you have to use numbers, not strings. Can you still animate transforms in RN or do I have to come up with some kind of sprite sheet and change the Image src at some fps?

Karl Taylor
  • 4,839
  • 3
  • 34
  • 62
sdfsdf
  • 5,052
  • 9
  • 42
  • 75

5 Answers5

213

You can actually animate strings using the interpolate method. interpolate takes a range of values, typically 0 to 1 works well for most things, and interpolates them into a range of values (these could be strings, numbers, even functions that return a value).

What you would do is take an existing Animated value and pass it through the interpolate function like this:

spinValue = new Animated.Value(0);

// First set up animation 
Animated.timing(
    this.spinValue,
  {
    toValue: 1,
    duration: 3000,
    easing: Easing.linear, // Easing is an additional import from react-native
    useNativeDriver: true  // To make use of native driver for performance
  }
).start()

// Next, interpolate beginning and end values (in this case 0 and 1)
const spin = this.spinValue.interpolate({
  inputRange: [0, 1],
  outputRange: ['0deg', '360deg']
})

Then use it in your component like this:

<Animated.Image
  style={{transform: [{rotate: spin}] }}
  source={{uri: 'somesource.png'}} />

In case if you want to do the rotation in loop, then add the Animated.timing in the Animated.loop

Animated.loop(
 Animated.timing(
   this.spinValue,
   {
    toValue: 1,
    duration: 3000,
    easing: Easing.linear,
    useNativeDriver: true
   }
 )
).start();
bogan27
  • 124
  • 1
  • 12
Nader Dabit
  • 52,483
  • 13
  • 107
  • 91
  • Nice! Simple and straight to the point! Also, in your rnplay example, how did you know start() could take a callback? Can't seem to find the docs for that on Facebook's RN website. – sdfsdf May 26 '16 at 19:51
  • Ah, yeah. I think I remember seeing browniefed (Jason Brown) post something about it. It does indeed take a callback though that gets fired when the animation completes. Check out https://github.com/browniefed/react-native-animation-book , and https://rnplay.org/browniefed . He's done a lot of examples with animations. I'm glad this helped :) – Nader Dabit May 26 '16 at 19:58
  • 13
    remember `this.state = { spinValue: new Animated.Value(0) }` – webjay Jun 22 '17 at 12:19
  • Created an example gist for this: https://gist.github.com/levynir/5962de39879a0b8eb1a2fd77ccedb2d8 – Nir Levy Jul 02 '17 at 09:30
  • 15
    Do use `loop` for making it loop infinitely ```Animated.loop( Animated.timing(this.spinValue, { toValue: 1, duration: 1000, easing: Easing.linear, useNativeDriver: true, }) ).start()``` – pradeep1991singh May 10 '18 at 04:50
  • Guys in this example we are rotating a image can we rotate custom html drawing of our own? For example i want to create a spinner in html and css and i want to rotate it using react native? Any ideas! – Salman Shaykh Sep 26 '18 at 07:27
  • Great! if I rotate 180deg, how can I rotate back ? – Nicks Apr 26 '20 at 03:35
  • @Nicks Start an animation that animates the value back to its initial value – ICW Jun 19 '21 at 16:21
  • Just wanted to point out, it is more readable to have the input range be equal to the output range. IE `inputRange: [0, 360]` and `outputRange: ['0deg', '360deg']` – ICW Jun 19 '21 at 16:22
47

Don't forget to add property useNativeDriver to ensure that you get the best performance out of this animation:

// First set up animation 
Animated.timing(
    this.state.spinValue,
  {
    toValue: 1,
    duration: 3000,
    easing: Easing.linear,
    useNativeDriver: true
  }
).start();
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
jeevium
  • 733
  • 6
  • 10
8

A note for the newbies like me: For animating something else you need to wrap it in <Animated.SOMETHING> for this to work. Or else the compiler will panic on that transform property:

import {Animated} from 'react-native';
...
//animation code above
...
<Animated.View style={{transform: [{rotate: spinValue}] }} >
   <YourComponent />
</Animated.View>

BUT for an image (Animated.Image), the example above is 100% goodness and correct.

Norfeldt
  • 8,272
  • 23
  • 96
  • 152
jeffski13
  • 581
  • 5
  • 6
2

Just gonna drop the solution I solved by stitching together parts from the answers here.

enter image description here

import { Feather } from '@expo/vector-icons'
import * as React from 'react'
import { TextStyle, Animated, Easing } from 'react-native'

import { Colors, FontSize } from '~/constants/Theme'

export const LoadingSpinner = React.memo(
  ({ color = Colors['sand'], size = FontSize['md'] - 1, fadeInDelay = 1000, ...props }: Props) => {
    const fadeInValue = new Animated.Value(0)
    const spinValue = new Animated.Value(0)

    Animated.sequence([
      Animated.delay(fadeInDelay),
      Animated.timing(fadeInValue, {
        toValue: 1,
        duration: 1500,
        easing: Easing.linear,
        useNativeDriver: true,
      }),
    ]).start()

    Animated.loop(
      Animated.timing(spinValue, {
        toValue: 360,
        duration: 300000,
        easing: Easing.linear,
        useNativeDriver: true,
      })
    ).start()

    return (
      <Animated.View
        style={{
          opacity: fadeInValue,
          transform: [{ rotate: spinValue }],
        }}
      >
        <Feather
          name="loader"
          size={size}
          style={{
            color,
            alignSelf: 'center',
          }}
          {...props.featherProps}
        />
      </Animated.View>
    )
  }
)

type Props = {
  color?: TextStyle['color']
  size?: number
  featherProps?: Partial<Omit<React.ComponentProps<typeof Feather>, 'style'>>
  fadeInDelay?: number
}

Hope it helps

Norfeldt
  • 8,272
  • 23
  • 96
  • 152
1

Since most of the answers are functions & hooks based, herewith a complete example of class based Animation of Image.

import React from 'react';
import {
  SafeAreaView,
  View,
  Animated,
  Easing,
  TouchableHighlight,
  Text,
} from 'react-native';

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      rotateValueHolder: new Animated.Value(0)
    };
  }
  componentDidMount = () => {
    this.startImageRotateFunction();
  }
  startImageRotateFunction = () => {
    Animated.loop(Animated.timing(this.state.rotateValueHolder, {
      toValue: 1,
      duration: 3000,
      easing: Easing.linear,
      useNativeDriver: false,
    })).start();
  };

render(){
    return(
      <SafeAreaView>
        <View>
          <Animated.Image
            style={{
              width: 200,
              height: 200,
              alignSelf:"center",
              transform:
                [
                  {
                    rotate: this.state.rotateValueHolder.interpolate(
                        {
                          inputRange: [0, 1],
                          outputRange: ['0deg', '360deg'],
                        }
                      )
                  }
                ],
            }}
              source={{uri:'https://raw.githubusercontent.com/AboutReact/sampleresource/master/old_logo.png',}}
          />
          <TouchableHighlight
            onPress={() => this.startImageRotateFunction()}>
            <Text style={{textAlign:"center"}}>
              CLICK HERE
            </Text>
          </TouchableHighlight>
        </View>
      </SafeAreaView>
    );
  }
}
Rahul Dasgupta
  • 463
  • 8
  • 14