React Native 动画(Animated)
一、前言
对于一个移动应用APP,其中的动画交互能够给用户带来很好的体验,所以动画在移动应用开发过程中是非常重要的;
二、React Native中实现动画的方式
-不断修改state
-Animated API
首先如果不使用任何动画API,那想到的实现动画效果的方式,应该就是通过不断修改state中的组件相关的属性值来实现动画效果,但是这个就是不断的重新渲染整个页面,所以可能会非常影响性能;
React Native中提供了Animated API来实现动画,Animated API可以简洁的实现各种动画和交互方式,并且具备极高的性能;
我们知道所谓动画,无非就是通过平移、缩放、旋转,透明度变换,以及这些基本变换的组合; React Native中也是通过Animated API 来实现对组件透明度style属性(opacity)以及style中transform属性的(translateX,translateY,rotate,scale)的修改来实现平移,缩放,旋转,透明度变换;
三、Animated 动画
-使用Animated 实现动画的步骤
(1)组件需要变化的属性必须使用Animated.Value或者Animated.ValueXY作为值
(2)需要进行动画的组件必须是Animated组件,Animated 提供了
Animated.View,Animated.Image,Animated.Text,Animated.ScrollView, Animated.FlatList, Anmated.SectionList这些基本的动画组件,但是也可以通过Animated.createAnimatedComponent()方法将任意组件变成Animated组件
(3)创建动画
Animated用于创建动画的方法:
Animated.timing():最常用的动画类型,使得值按照过渡曲线随时间变化
Animated.spring():弹簧变化效果
Animated.decay():衰变效果,以一个初始的速度和一个衰减系数逐渐减慢为 0
(4)创建动画之后调用start()方法,stop()方法开启和结束动画
以上就是Animated实现动画的基本步骤
-动画示例
下面我们就按照上面的步骤,来实现透明度,平移,缩放,旋转动画;
透明度动画:
import React from 'react';
import { Animated, Text, View,
Button
} from 'react-native';
export default class AnimatedViewScreen extends React.Component {
fadeInOpacity= new Animated.Value(0) //透明度初始值设为0
componentDidMount() {
Animated.timing( //随时间变化而执行动画
this.fadeInOpacity, //动画中的变量值
{
toValue: 1, // 透明度最终变为1,即完全不透明
duration: 3000, //动画时长
}
).start(); //开始执行动画
}
render() {
return (
<View style={{flex:1,justifyContent:'center',alignItems:'center'}}>
<Animated.View style={{width:100,height:100,opacity:this.fadeInOpacity}}>
<Button title='透明度 按钮'/>
</Animated.View>
</View>
}
}
平移动画:
import React from 'react';
import { Animated, Text, View,
Button
} from 'react-native';
export default class AnimatedViewScreen extends React.Component {
translateX=new Animated.Value(0)
componentDidMount() {
Animated.timing(this.translateX,
{
toValue:150,
duration:3000,
}
).start();
}
render() {
return (
<View style={{flex:1,justifyContent:'center',alignItems:'center'}}>
<Animated.View style={{width:100,height:100,transform:[{translateX:this.translateX}]}}>
<Button title='平移按钮'/>
</Animated.View>
</View>
}
}
旋转动画:
import React from 'react';
import { Animated, Text, View,
Button
} from 'react-native';
export default class AnimatedViewScreen extends React.Component {
degree=new Animated.Value(0)
componentDidMount() {
Animated.timing(this.degree,
{
toValue:1,
duration:3000,
}
).start();
}
render() {
const realDeg=this.degree.interpolate({
inputRange:[0,1],
outputRange:['0deg','360deg']
});
return (
<View style={{flex:1,justifyContent:'center',alignItems:'center'}}>
<Animated.Image style={{width:100,height:100,transform:[{rotate:realDeg}],
}} source={require('./image/tab_b_select.png')}>
</Animated.Image>
</View>
}
}
这里我们使用了一个interpolate()插值函数,用于将输入值范围转换为输出值范围这里是将[0,1] 输入 转为[‘0deg’,‘360deg’];
interpolate变化过程也可以是多段的输入[0,0.5,1] ,输出为[‘0deg’,‘360deg’,‘0deg’]
缩放动画:
import React from 'react';
import { Animated, Text, View,
Button
} from 'react-native';
export default class AnimatedViewScreen extends React.Component {
scale=new Animated.Value(0)
componentDidMount() {
Animated.timing(this.scale,
{
toValue:1,
duration:3000,
}
).start();
}
render() {
return (
<View style={{flex:1,justifyContent:'center',alignItems:'center'}}>
<Animated.View style={{width:100,height:100,transform:[{scale:this.scale}]}}>
<Button title='缩放按钮'/>
</Animated.View>
</View>
}
}
-组合动画
上面已经介绍了基本动画,透明度,平移,旋转,缩放,那么复杂的动画就需要这些基本变换的组合,也就是对这些基本动画的组合。
Animated API中提供了以下方法来实现组合动画:
Animated.parallel():参数是动画数组,同时开始动画数组中的全部动画。默认情况下有一个动画停止了,其余的也会停止,可以通过stopTogether选项设置false,来取消这种关联。
Animated.sequence():参数是动画数组,按照顺序执行动画中的所有数组,前一个动画执行完成后,后面一个动画再开始,如果当前动画被中止,则后面的动画不会继续执行;
Animated.stagger():参数是一个延迟时间和一个动画数组,前一个动画开始之后,一段时间后(延迟时间后),后一个动画开始;因为仅仅是前一个动画开始后指定时间,前一个动画并不一定结束了,所以会出现同时执行(重叠)的情况;
组合动画示例:
import React from 'react';
import { Animated, Text, View,
Button
} from 'react-native';
export default class AnimatedViewScreen extends React.Component {
sameTimeDegree=new Animated.Value(0)
sameTimeScale=new Animated.Value(0)
componentDidMount() {
//释放出来看效果
// Animated.parallel([
// this.createAnimation(this.sameTimeScale,3000),
// this.createAnimation(this.sameTimeDegree,3000)
// ]).start()
//释放出来看效果
// Animated.sequence([
// this.createAnimation(this.sameTimeScale,3000),
// this.createAnimation(this.sameTimeDegree,3000)
// ]).start()
Animated.stagger(10000,[
this.createAnimation(this.sameTimeScale,3000),
this.createAnimation(this.sameTimeDegree,3000)
]).start()
}
render() {
const realSameTimeDeg=this.sameTimeDegree.interpolate({
inputRange:[0,1],
outputRange:['0deg','360deg']
})
return (
<View style={{flex:1,justifyContent:'center',alignItems:'center'}}>
<Animated.Image
style={{
width:100,
height:100,
transform:[
{
scale:this.sameTimeScale
},
{rotate:realSameTimeDeg}
]
}}
source={require('./image/tab_b_select.png')}
>
</Animated.Image>
</View>
}
}
-一些注意点
(1)如何使动画循环执行
我们可能需要一个一直旋转的View;
Animated.start()方法可以传入一个参数,这个参数是一个函数,在动画结束时
会回调这个函数,我们可以在这个函数里将属性值重新设置为初始值,然后再启动动画,以此来实现动画的循环执行;
循环动画示例:
import React from 'react';
import {
Animated,
View,
} from 'react-native';
export default class ProgressBar extends Component{
deg=new Animated.Value(0)
rotate=Animated.timing(this.deg, {
toValue: 1, //属性目标值
duration: 1000, //动画执行时间
easing: Easing.linear
})
componentDidMount() {
this.startAnimate()
}
startAnimate(){
this.deg.setValue(0)
this.rotate.start(()=>{this.startAnimate()})
}
render() {
const realDeg=this.deg.interpolate({
inputRange:[0,1],
outputRange:[0,360]
});
return (
<View style={{flex:1,justifyContent:'center',alignItems:'center'}}>
<Animated.Image style={{width:100,height:100,transform: [{rotate:realDeg}],
}} source={require('./image/tab_b_select.png')}>
</Animated.Image>
<View/>
)
}
}
上面代码就是通过start()启动动画时传入函数,重新调用startAnimation()方法设置属性值为初始值,并重新启动动画实现动画循环执行,从而实现了一个一直旋转的图片这里要注意的是,Animated.timing()方法中easing:Easing.linear属性,也就是设置动画线性匀速播放,否则动画一次播放结束时,进入下一次动画时可能并不是连续的,会有停顿;