反应本机: 你如何动画一个图像的旋转?

旋转是一种样式转换,在 RN 中,你可以像这样旋转

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

但是,要在 RN 中动画事物,您必须使用数字,而不是字符串。你仍然可以在 RN 动画变换或者我必须拿出一些精灵表和改变图像 src 在一些 fps?

101280 次浏览

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();

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();

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.

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>
);
}
}

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 👍