React Native 动画
1.Animated.timing()
最常用的动画类型,是一个值按照一个过渡曲线而随时间变化,格式如下:Animated.timing(animateValue,conf<Object>),conf参数格式:
{
duration:动画持续的时间(单位是毫秒),默认为500。
easing:一个用户定义曲线的渐变函数。Easing模预置了linear、ease、elastic、bezier等诸多欢动特性。iOS默认为Easing.inOut(Easing.ease)。
delay:开始动画前的延迟时间(毫秒),默认为0。
}
export default class Opacity extends Component {
constructor(props) {
super(props)
this.state = {
fadeOutOpacity: new Animated.Value(1),
};
this.fadeOutAnimated = Animated.timing(
this.state.fadeOutOpacity,
{
toValue: 0,//透明度动画最终值
duration: 3000,//动画时长3000毫秒
easing: Easing.linear,
}
);
}
_startAnimated() {
this.fadeOutAnimated.start(() => this.state.fadeOutOpacity.setValue(1));
}
render() {
return (
<View style={styles.container}>
<Animated.View style={{ width: 360, height: 300, opacity: this.state.fadeOutOpacity }}>
<Image ref="iamge" style={{ width: 360, height: 300 }} source={require('./beauty.png')}>
</Image>
</Animated.View>
<TouchableOpacity style={styles.touchStyle} onPress={this._startAnimated.bind(this)}>
<Text style={{width:200,height:100,textAlign:'center',lineHeight:100}}>点击开始动画</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#ecf0f1',
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
touchStyle:{
flex:1,
justifyContent:'center',
alignItems:'center',
textAlign:'center',
alignSelf:'center',
},
});
![](https://img.haomeiwen.com/i4193251/5beb03931af6b4bc.gif)
interpolate()函数
export default class Mixture extends Component {
constructor(props) {
super(props)
this.state = {
animatedValue: new Animated.Value(0),
}
this.rotateAnimated = Animated.timing(
this.state.animatedValue,
{
toValue: 1,
duration: 3000,
easing: Easing.in,
}
);
}
_startAnimated() {
this.state.animatedValue.setValue(0);
this.rotateAnimated.start(() => this._startAnimated());
}
render() {
const rotateZ = this.state.animatedValue.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg']
});
const opacity = this.state.animatedValue.interpolate({
inputRange: [0, 0.5, 1],
outputRange: [0, 1, 0]
});
const rotateX = this.state.animatedValue.interpolate({
inputRange: [0, 0.5, 1],
outputRange: ['0deg', '180deg', '0deg']
});
const textSize = this.state.animatedValue.interpolate({
inputRange: [0, 0.5, 1],
outputRange: [18, 32, 18]
});
const marginLeft = this.state.animatedValue.interpolate({
inputRange: [0, 0.5, 1],
outputRange: [0, 200, 0]
});
return (
<View style={styles.container}>
<Animated.View style={{
marginTop: 10,
width: 100,
height: 100,
transform: [{ rotateZ: rotateZ },]
}}>
<Image style={{ width: 100, height: 100 }}
source={require('./stream.jpg')}
>
</Image>
</Animated.View>
<Animated.View
style={{
marginTop: 2,
width: 100,
height: 100,
opacity: opacity,
backgroundColor: 'red',
}}
/>
<Animated.Text
style={{
mariginTop: 2,
width: 100,
fontSize:18,
color:'white',
backgroundColor:'red',
transform:[{rotateX:rotateX}]
}}
>
红酥手,黄藤酒,满城春色宫墙柳。
</Animated.Text>
<Animated.Text
style={{
marginTop:2,
height:100,
lineHeight:100,
fontSize:textSize,
color:'red'
}}
>
春如旧,人空瘦,泪痕红浥鲛绡透。
</Animated.Text>
<Animated.View
style={{
marginTop:2,
width:100,
height:100,
marginLeft:marginLeft,
backgroundColor:'red',
}}
/>
<TouchableOpacity style={styles.touchStyle} onPress={this._startAnimated.bind(this)}>
<Text style={{width:200,height:100,textAlign:'center',lineHeight:100}}>点击开始动画</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#ecf0f1',
padding: 8,
},
touchStyle:{
flex:1,
marginTop:6,
justifyContent:'center',
alignItems:'center',
textAlign:'center',
alignSelf:'center',
},
});
![](https://img.haomeiwen.com/i4193251/dea9151d86a7e683.gif)
2.Animated.spring()
弹簧效果,基础的单次弹跳物理模型实现的 spring 动画,格式如下:
Animated.spring(animateValue, conf<Object>),conf参数格式:
{
friction: 控制“弹跳系数”、夸张系数,默认为7。
tension: 控制速度,默认为40。
speed: 控制动画的速度,默认为12。
bounciness: 反弹系数,默认为8。
}
export default class AnimatedSpring extends Component {
constructor(props) {
super(props);
this.state = {
springValue: new Animated.Value(1),
};
this.springAnimated = Animated.spring(
this.state.springValue,
{
toValue: 1,
friction: 2,//弹跳系数
tension: 10,//控制速度
}
);
};
_startAnimated() {
this.state.springValue.setValue(0.1);
this.springAnimated.start();
}
render() {
return (
<View style={styles.container}>
<Animated.View
style={{
width: 282,
height: 152,
transform: [{ scale: this.state.springValue }]
}}
>
<Image ref='image' style={{ width: 282, height: 152 }}
source={require('./scenery.jpg')}
>
</Image>
</Animated.View>
<TouchableOpacity style={styles.touchStyle} onPress={this._startAnimated.bind(this)}>
<Text style={{width:200,height:100,textAlign:'center',lineHeight:100}}>点击开始动画</Text>
</TouchableOpacity>
</View>
)
}
}
const styles = StyleSheet.create({
container:{
flex:1,
justifyContent:'center',
alignItems:'center',
backgroundColor:'#ecf0f1',
padding:8,
width:'100%',
height:'100%',
},
touchStyle:{
flex:1,
justifyContent:'center',
alignItems:'center',
textAlign:'center',
alignSelf:'center',
marginTop:6,
},
});
![](https://img.haomeiwen.com/i4193251/3819c5dafa410e59.gif)
3.Animated.decay()
衰变效果,以一个初始的速度和一个衰减系数逐渐减慢变为0,格式如下:
Animated.decay(animateValue, conf<Object>),conf参数格式:
{
velocity: 起始速度,必填参数。
deceleration: 速度衰减比例,默认为0.997。
}
export default class AnimatedDecay extends Component {
constructor(props) {
super(props);
this.state = {
decayValue: new Animated.ValueXY({x:0,y:0}),
};
this.decayAnimated = Animated.decay(
this.state.decayValue,
{
velocity: 5, // 起始速度,必填
deceleration: 0.95, // 速度衰减比例,默认为0.997
}
);
}
_startAnimated() {
this.decayAnimated.start();
}
render(){
return (
<View style={styles.mainStyle}>
<Animated.View
style={{
width: 100,
height: 150,
transform:[
{translateX: this.state.decayValue.x}, // x轴移动
{translateY: this.state.decayValue.y}, // y轴移动
]
}}
>
<Image ref="image" style={{width:100,height:150}}
source={{uri:'beauty.jpg'}}>
</Image>
</Animated.View>
<TouchableOpacity style={styles.touchStyle} onPress={this._startAnimated.bind(this)}>
<Text style={{width:200,height:100,textAlign:'center',lineHeight:100}}>点击开始动画</Text>
</TouchableOpacity>
</View>
);
}
}
![](https://img.haomeiwen.com/i4193251/841852b560c86f72.gif)
4.Animated.parallel()
同时开始一个动画数组里的全部动画。默认情况下,如果有任何一个动画停止了,其余的也会被停止。可以通过stopTogether 选项设置为 false 来取消这种关联,格式如下:
Animated.parallel(Animates<Array>, [conf<Object>]):,conf参数格式:
{
stopTogether: false
}
第一个参数接受一个元素为动画的数组,通过执行 start() 方法可以并行执行该数组中的所有方法。如果数组中任意动画被中断的话,该数组内对应的全部动画会一起停止,不过我们可以通过第二个(可选)参数 conf 中的 stopTogether 来取消这种牵连特性。
export default class AnimatedParallel extends Component {
constructor(props) {
super(props);
this.state = {
dogOpacityValue: new Animated.Value(1),
dogACCValue : new Animated.Value(0)
};
this.parallelAnimated = Animated.parallel(
[
Animated.timing(
this.state.dogOpacityValue,
{
toValue: 1,
duration: 1000,
}
),
Animated.timing(
this.state.dogACCValue,
{
toValue: 1,
duration: 2000,
easing: Easing.linear,
}
),
],
{
stopTogether: false
}
);
}
_startAnimated() {
this.state.dogOpacityValue.setValue(0);
this.state.dogACCValue.setValue(0);
this.parallelAnimated.start();
}
render(){
//透明度
const dogOpacity = this.state.dogOpacityValue.interpolate({
inputRange: [0,0.2,0.4,0.6,0.8,1],
outputRange: [0,1,0,1,0,1]
});
//项链上面
const neckTop = this.state.dogACCValue.interpolate({
inputRange: [0, 1],
outputRange: [350, 235]
});
//眼镜左边
const left = this.state.dogACCValue.interpolate({
inputRange: [0, 1],
outputRange: [-120, 127]
});
//眼镜旋转
const rotateZ = this.state.dogACCValue.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg']
});
return (
<View style={styles.container}>
{/*// 狗头*/}
<Animated.View
style={{
width: 375,
height: 240,
opacity:dogOpacity,
}}
>
<Image ref="image" style={{width:375,height:242}}
source={require('./dog.jpg')}>
</Image>
</Animated.View>
{/*// 项链*/}
<Animated.View
style={{
width: 250,
height: 100,
position: 'absolute',
top:neckTop,
left:93,
}}
>
<Image ref="image" style={{width:250,height:100,resizeMode:'stretch'}}
source={require('./necklace.jpg')}>
</Image>
</Animated.View>
<View
style={{
width: 375,
height: 200,
backgroundColor:'white',
}}
/>
{/*// 眼镜*/}
<Animated.View
style={{
width: 120,
height: 25,
position: 'absolute',
top:160,
left:left,
transform:[
{rotateZ:rotateZ}
],
}}
>
<Image ref="image" style={{width:120,height:25,resizeMode:'stretch'}}
source={require('./glasses.png')}>
</Image>
</Animated.View>
<TouchableOpacity style={styles.touchStyle} onPress={this._startAnimated.bind(this)}>
<Text style={{width:200,height:100,textAlign:'center',lineHeight:100}}>点击开始动画</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex:1,
backgroundColor:"#ffffff",
justifyContent:'flex-start',
alignItems:'flex-start',
paddingTop:100,
width:'100%',
height:'100%',
},
touchStyle: {
width:200,
height:100,
position:'absolute',
bottom:0,
left:screenW/2 - 100,
},
});
![](https://img.haomeiwen.com/i4193251/f4eaf83cb8c32c13.gif)
5.Animated.sequence()
按顺序执行一个动画数组里的动画,等待一个完成后再执行下一个。如果当前的动画被中止,后面的动画则不会继续执行,格式如下:
Animated.sequence(Animates<Array>)
export default class AnimatedSequence extends Component {
constructor(props) {
super(props);
this.state = {
turnRotateValue: new Animated.Value(0),
turnShakeValue : new Animated.Value(0),
macValue : new Animated.Value(0),
};
this.sequenceAnimated = Animated.sequence(
[
Animated.timing(
this.state.turnRotateValue,
{
toValue: 1,
duration: 5000,
easing: Easing.in,
}
),
Animated.timing(
this.state.turnShakeValue,
{
toValue: 1,
duration: 500,
easing: Easing.in,
delay:300,
}
),
Animated.spring(
this.state.macValue,
{
toValue: 1,
friction: 3,
tension:10,
}
),
]
);
}
_startAnimated() {
this.sequenceAnimated.start();
}
render(){
//转盘旋转
const turnRotateZ = this.state.turnRotateValue.interpolate({
inputRange: [0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1],
outputRange: [
'0deg',
'180deg',
'360deg',
'720deg',
'1080deg',
'1800deg',
'2520deg',
'3060deg',
'3420deg',
'3600deg',
'3690deg',
]
});
//转盘震动
const marginLeft = this.state.turnShakeValue.interpolate({
inputRange: [0,0.2,0.4,0.6,0.8,1],
outputRange: [0,-40,40,-40,40,0]
});
//MacTop
const macTop = this.state.macValue.interpolate({
inputRange: [0, 1],
outputRange: [-200,150]
});
return (
<View style={styles.mainStyle}>
{/*// 转盘*/}
<Animated.View
style={{
width: 300,
height: 300,
marginLeft:marginLeft,
transform:[
{rotateZ:turnRotateZ}
],
}}
>
<Image ref="image" style={{width:300,height:300}}
source={require('./scenery.jpg')}>
</Image>
</Animated.View>
{/*// mac*/}
<Animated.View
style={{
width: 300,
height: 204,
position: 'absolute',
top:macTop,
left:screenW / 2 - 150,
}}
>
<Image ref="image" style={{width:300,height:204}}
source={require('./landscape.jpg')}>
</Image>
</Animated.View>
<TouchableOpacity style={styles.touchStyle} onPress={this._startAnimated.bind(this)}>
<Text style={{width:200,height:100,textAlign:'center',lineHeight:100}}>点击开始动画</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex:1,
width:screenW,
backgroundColor:"#ffffff",
justifyContent:'flex-start',
alignItems:'flex-start',
paddingTop:100,
width:'100%',
height:'100%',
},
touchStyle: {
width:200,
height:100,
bottom:0,
left:screenW/2 - 100,
marginTop:60,
},
});
![](https://img.haomeiwen.com/i4193251/2538aec163a885f4.gif)
6.Animated.stagger()
一个动画数组,传入一个时间参数来设置队列动画间的延迟,即在前一个动画开始之后,隔一段指定时间才开始执行下一个动画里面的动画,并不关心前一个动画是否已经完成,所以有可能会出现同时执行(重叠)的情况,其格式如下:
Animated.stagger(delayTime<Number>, Animates<Array>)
其中 delayTime 为指定的延迟时间(毫秒),第二个和上面两个一样传入一个动画事件数组。
export default class AnimatedStagger extends Component {
constructor(props) {
super(props);
this.state = {
redValue: new Animated.Value(0),
blueValue : new Animated.Value(0),
};
this.staggerAnimated = Animated.stagger(2000,
[
Animated.timing(
this.state.redValue,
{
toValue: 1,
duration: 5000,
easing: Easing.in,
}
),
Animated.timing(
this.state.blueValue,
{
toValue: 1,
duration: 5000,
easing: Easing.in,
}
),
]
);
}
_startAnimated() {
this.staggerAnimated.start();
}
render(){
const redMarginLeft = this.state.redValue.interpolate({
inputRange: [0,1],
outputRange: [0,200]
});
const blueMarginLeft = this.state.blueValue.interpolate({
inputRange: [0,1],
outputRange: [0,200]
});
return (
<View style={styles.mainStyle}>
{/*// 红色*/}
<Animated.View
style={{
width: 100,
height: 100,
backgroundColor:'red',
marginLeft:redMarginLeft,
}}
>
</Animated.View>
{/*// 蓝色*/}
<Animated.View
style={{
width: 100,
height: 100,
backgroundColor:'blue',
marginLeft:blueMarginLeft,
}}
>
</Animated.View>
<TouchableOpacity style={styles.touchStyle} onPress={this._startAnimated.bind(this)}>
<Text style={{width:200,height:100,textAlign:'center',lineHeight:100}}>点击开始动画</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex:1,
width:screenW,
backgroundColor:"#ffffff",
justifyContent:'flex-start',
alignItems:'flex-start',
paddingTop:100,
width:'100%',
height:'100%',
},
touchStyle: {
width:200,
height:100,
bottom:0,
left:screenW/2 - 100,
},
});
![](https://img.haomeiwen.com/i4193251/d972966993e87ebb.gif)