我们知道执行一个动画最基本的原理就是调用动画函数改变一个变量值,而这个变量值绑定到控件的属性上,比如透明的、位置等。通过动画函数改变值从而使控件动起来。所以最基本的动画要有2个属性:
- 变量值(Animated.Value和Animated.ValueXY)
- 动画类型:timing、spring、decay
我们先看一个最基本的demo,这个demo实现了平移动画
/**
* Created by on 2017/5/26.
*/
import React, {Component} from 'react';
import {
StyleSheet,
View,
ScrollView,
Text,
Button,
Animated,
Alert,
} from 'react-native';
export default class AnimatedDemo extends Component {
static navigationOptions = {
title: 'Animated'
}
state = {
left:new Animated.Value(0),
}
_translateAni = ()=>{
this.state.left.setValue(0);
Animated.timing(
this.state.left,
{
toValue: 300,
duration: 1000,
},
).start();
}
render() {
return (
<View style={{flex:1}}>
<Button title='平移' onPress={()=>{this._translateAni()}}/>
<Animated.View
style={{width:50,height:100,backgroundColor:'blue',left:this.state.left}}>
</Animated.View>
</View>
);
}
}
要想写一个动画需要以下几步:
1、绑定需要动画的控件(Animated.View),并不是所有的组件都可以,只有View,Text,Image、通过Animated.createAnimatedComponent自定义创建的动画组件。
2、将动画变量值绑定到View style 的left属性上,这样看起来View就会向右移动。
3、启动动画:Animated.timing(…).start();
下面我们详细的说说
Animated.Value和Animated.ValueXY
Animated.Value和Animated.ValueXY就是上面我们所说的变量值即动画执行过程中会不断的更改这个值。Animated.Value:代表单个值,Animated.ValueXY代表(x,y)值或者也可以叫向量值。
动画的变量值初始化:left:new Animated.Value(0),不能这样初始化left:0,Animated.Value提供了以下方法:
- constructor:构造函数。new Animated.Value(0)
- setValue:设置新的值,注意此方法会停止动画
- setOffset:设置一个修正值,不论接下来的值是由setValue、一个动画,还是Animated.event产生的,都会加上这个值。常用来在拖动操作一开始的时候用来记录一个修正值(譬如当前手指位置和View位置)。
- flattenOffset:该用来把相对值合并到值里,然后相对值设置为0,最终输出的值不会发生变化。常常在拖动操作结束以后调用。
- addListener:异步监听动画值变化
- removeListener:删除指定监听器
- removeAllListeners:删除所有监听器
- stopAnimation:停止动画,返回当前动画值。
- interpolate:差值,可以将值映射成新的值,后面会具体介绍。
Animated.ValueXY其实是就是2个Animated.Value,方法和Animated.Value一样,不过比Animated.Value 多了2个方法:
getLayout:将一个{x, y} 组合转换为一个可用的位移变换(translation transform),例如:
style={{
transform: this.state.anim.getTranslateTransform()
}}getTranslateTransform:将一个{x, y} 组合转换为一个可用的位移变换(translation transform),例如:
style={{
transform: this.state.anim.getTranslateTransform()
}}
动画类型:timing、spring、decay
timing:值按照一个过渡曲线而随时间变化,原代码:
var timing = function(
value: AnimatedValue | AnimatedValueXY,
config: TimingAnimationConfig,
)
从原代码我们可以看出timing函数有2个参数:
1、第一个动画值,类型是AnimatedValue 或者AnimatedValueXY
2、第二个是配置参数,TimingAnimationConfig,TimingAnimationConfig原代码:
type TimingAnimationConfig = AnimationConfig & {
toValue: number | AnimatedValue | {x: number, y: number} | AnimatedValueXY,
easing?: (value: number) => number,
duration?: number,
delay?: number,
};type AnimationConfig = {
isInteraction?: bool,
useNativeDriver?: bool,
onComplete?: ?EndCallback,
};
从原代码我们看出TimingAnimationConfig可以配置如下属性:
- toValue:最终值,动画值从初始之变化到此值。
- easing:曲线(动画)函数,默认Easing.inOut
- duration:持续时间,默认500
- delay:延迟时间,默认0
- useNativeDriver:是否使用原生驱动,默认false
- isInteraction:????
- onComplete:完成回调
咱们再来看看Easing,系统为我们定义了很多动画函数,原代码 ,大家可以看看官方注解,感觉语言说不太清楚,大家可以试试效果就明白了。
spring:弹簧动画,原代码:
var spring = function(
value: AnimatedValue | AnimatedValueXY,
config: SpringAnimationConfig,
)
依然需要2个参数:
1、动画值:同timing
2、SpringAnimationConfig,原代码:
type SpringAnimationConfig = AnimationConfig & {
toValue: number | AnimatedValue | {x: number, y: number} | AnimatedValueXY,
overshootClamping?: bool,
restDisplacementThreshold?: number,
restSpeedThreshold?: number,
velocity?: number | {x: number, y: number},
bounciness?: number,
speed?: number,
tension?: number,
friction?: number,
};
和TimingAnimationConfig一样这里不说了,这里说下几个重要的属性:
- friction:摩擦系数,默认7
- tension:张力,默认40
其他的可以不考虑。
decay:带有加速度值的动画,原代码:
var decay = function(
value: AnimatedValue | AnimatedValueXY,
config: DecayAnimationConfig,
)
DecayAnimationConfig原代码:
type DecayAnimationConfig = AnimationConfig & {
velocity: number | {x: number, y: number},
deceleration?: number,
};
- velocity:初始速度
- deceleration:衰减速率,默认0.997
这一章我们简单的介绍了基本动画,下一章我们复杂 的动画。
/**
* Created by on 2017/5/26.
*/
import React, {Component} from 'react';
import {
StyleSheet,
View,
ScrollView,
Text,
Button,
Animated,
Alert,
} from 'react-native';
export default class AnimatedDemo extends Component {
static navigationOptions = {
title: 'Animated'
}
state = {
animatedValue: new Animated.Value(0),
decayValue: new Animated.Value(0),
left:new Animated.Value(0),
}
_beforeValue = 0;
_startAnimated = (type) => {
this.state.animatedValue.setValue(0);
if ('timing' === type) {
Animated.timing(
this.state.animatedValue,
{
toValue: 1,
duration: 2000,
},
).start();
} else if ('spring' === type) {
Animated.spring(
this.state.animatedValue,
{
toValue: 1,
duration: 2000,
},
).start();
} else if ('decay' === type) {
Animated.decay(
this.state.decayValue,
{
velocity: 2,
deceleration:0.992,
},
).start(() => {
});
}
this.state.decayValue.addListener((callback) => {
var n = callback.value - this._beforeValue;
this._beforeValue = callback.value;
console.log(callback.value+'----'+n);
})
}
_translateAni = ()=>{
this.state.left.setValue(0);
Animated.timing(
this.state.left,
{
toValue: 300,
duration: 1000,
},
).start();
}
render() {
return (
<View style={{flex:1}}>
<Button title='平移' onPress={()=>{this._translateAni()}}/>
<Animated.View
style={{width:50,height:100,backgroundColor:'blue',left:this.state.left}}>
</Animated.View>
<View style={{flexDirection:'row'}}>
<View>
<Button title='开始动画:timing' onPress={()=>{this._startAnimated('timing')}}/>
<Button title='开始动画:spring' onPress={()=>{this._startAnimated('spring')}}/>
<Button title='开始动画:decay' onPress={()=>{this._startAnimated('decay')}}/>
</View>
<Animated.View
style={{width:200,height:100,backgroundColor:'red',
transform:[
{translateY:this.state.animatedValue.interpolate({
inputRange:[0,1],
outputRange:[0,100]
})},
]}}>
</Animated.View>
</View>
<Animated.View
style={{width:200,height:100,backgroundColor:'blue',
transform:[
{translateY:this.state.decayValue},
]}}>
</Animated.View>
</View>
);
}
}