简述
react native
封装了两个易于使用的动画组件。用于全局的布局动画LayoutAnimation
,和用于创建更精细的交互控制的动画Animated
。本章主要对Animated
组件进行简单的学习。
动画类型
Animated
提供了三种动画类型。每种动画类型都提供了特定的函数曲线,用于控制动画值从初始值变化到最终值的变化过程:
-
Animated.decay()
以指定的初始速度开始变化,然后变化速度越来越慢直至停下。 -
Animated.spring()
提供一个简单的弹簧物理模型。 -
Animated.timing()
使用easing函数让数据随时间动起来。
大多数情况下你应该使用timing()
。默认情况下,它使用对称的 easeInOut
曲线,将对象逐渐加速到全速,然后通过逐渐减速停止结束。
以下是三种动画类型的效果对比:
方法详解
1. Animated.decay(animateValue, conf)
推动一个值以一个初始的速度和一个衰减系数逐渐变为0。conf参数有以下这些属性:
-
velocity
: 初始速度。必填。 -
deceleration
: 衰减系数。默认值0.997。 -
useNativeDriver
: 使用原生动画驱动。默认不启用(false)。
2. Animated.spring(animateValue, conf)
基础的单次弹跳物理模型,产生一个基于Rebound
和Origami
实现的Spring
动画。它会在toValue
值更新的同时跟踪当前的速度状态,以确保动画连贯。可以链式调用。
conf参数有以下这些属性(注意你不能同时定义bounciness
/speed
和 tension
/friction
这两组,只能指定其中一组):
-
friction
: 控制“弹跳系数”、夸张系数,默认为7. -
tension
: 控制速度,默认40。 -
speed
: Controls speed of the animation. Default 12. -
bounciness
: Controls bounciness. Default 8. -
useNativeDriver
: 使用原生动画驱动。默认不启用(false)。
3. Animated.timing(animateValue, conf)
推动一个值按照一个过渡曲线而随时间变化。conf参数有以下这些属性:
-
duration
: 动画的持续时间(毫秒)。默认值为500. -
easing
: 一个用于定义曲线的渐变函数。阅读Easing模块可以找到许多预定义的函数。默认值为Easing.inOut(Easing.ease). -
delay
: 开始动画前的延迟时间(毫秒)。默认为0. -
useNativeDriver
: 使用原生动画驱动。默认不启用(false)。
这里提及的 Easing
动画函数模块在 react-native/Libraries/Animated/src/
目录下,该模块预置了 ease
、bezier
等诸多缓动特性,有兴趣可以去了解。
代码
import React from 'react';
import {
View,
Text,
Easing,
Animated,
} from 'react-native';
export default class FadeInView extends React.Component {
constructor(props) {
super(props);
this.state = {
fadeAnim: new Animated.Value(0), // 透明度初始值设为0
};
}
componentDidMount() {}
startAnimated(type){
if (type==0) {
Animated.decay( // 推动一个值以一个初始的速度和一个衰减系数逐渐变为0。
this.state.fadeAnim,
{
toValue: 1, // 透明度最终变为1,即完全不透明
velocity: 0.1 // 初始速度
}
).start(); // 开始执行动画
}else if (type==1) {
Animated.spring( // 基础的单次弹跳物理模型
this.state.fadeAnim,
{
toValue: 1, // 透明度最终变为1,即完全不透明
friction: 8, // 弹性系数
tension: 35 // 速度
}
).start(); // 开始执行动画
}else{
Animated.timing( // 随时间变化而执行的动画类型
this.state.fadeAnim,
{
toValue: 1, // 透明度最终变为1,即完全不透明
duration: 2500, // 动画时间
easing: Easing.bezier(0.15, 0.73, 0.37, 1.2) // 曲线的渐变函数。
}
).start((res)=>{ // 开始执行动画
console.log(this.TAG,'res',JSON.stringify(res))
});
}
}
render() {
return (
<View>
<Animated.View
style={{
style={this.props.style,{
backgroundColor: 'powderblue',
opacity: this.state.fadeAnim, // 将透明度指定为动画变量值
}}>
{this.props.children}
</Animated.View>
</View>
);
}
}
'use strict';
import React from 'react';
import {
StyleSheet,
View,
Text,
TouchableOpacity
} from 'react-native';
import FadeInView from './fadeInView';
export default class TestAnimated extends React.Component {
constructor(props) {
super(props);
this.state = {
}
}
startAnimated(){
this.animatedOfDecay&&this.animatedOfDecay.startAnimated(0);
this.animatedOfSpring&&this.animatedOfSpring.startAnimated(1);
this.animatedOfTiming&&this.animatedOfTiming.startAnimated(2);
}
render() {
return (
<View style={{flex:1,alignItems:'center'}}>
<FadeInView style={styles.fadeView} ref={(o)=>this.animatedOfDecay=o}>
<Text style={styles.fadeInText}>decay</Text>
</FadeInView>
<FadeInView style={styles.fadeView} ref={(o)=>this.animatedOfSpring=o}>
<Text style={styles.fadeInText}>spring</Text>
</FadeInView>
<FadeInView style={styles.fadeView} ref={(o)=>this.animatedOfTiming=o}>
<Text style={styles.fadeInText}>timing</Text>
</FadeInView>
<TouchableOpacity onPress={()=>this.startAnimated()}>
<Text style={{fontSize:Size(16),padding:10}}>start</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
fadeInView:{
width:250,
height:50,
justifyContent:'center',
alignItems:'center',
},
fadeInText:{
fontSize:28,
textAlign:'center',
margin:10,
},
});
使用动画
通过在动画上调用start()
来启动动画。 start()
需要一个 完成 回调函数,当动画完成时将会调用它。如果动画运行正常,则将通过{finished:true}
触发回调。如果动画是因为调用了stop()
而结束(例如,因为它被手势或其他动画中断),则它会收到{finished:false}
。
Animated.timing(// 随时间变化而执行的动画类型
this.state.fadeAnim, // 动画中的变量值
{
toValue: 1, // 透明度最终变为1,即完全不透明
duration: 2500, // 动画时间
}
).start((res)=>{
console.log(this.TAG,'res',JSON.stringify(res)); //FadeInView res {"finished":true}
// todo 做一些事情
});
使用原生动画驱动
使用原生动画,我们会在开始动画之前将所有关于动画的内容发送到原生代码,从而使用原生代码在UI线程上执行动画,而不是通过对每一帧的桥接去执行动画。一旦动画开始,JS线程就可以在不影响动画效果的情况下阻塞(去执行其他任务)掉了。
你可以通过在动画配置中指定useNativeDriver:true
来使用原生动画驱动。你可以在官方的 动画文档 中看到更详细的解释。
Animated.timing(this.state.animatedValue, {
toValue: 1,
duration: 500,
useNativeDriver: true, // <-- 加上这一行
}).start();
动画值在不同的驱动方式之间是不能兼容的。因此如果你在某个动画中启用了原生驱动,那么所有和此动画依赖相同动画值的其他动画也必须启用原生驱动。
自定义动画组件
Animated
仅封装了四个可以动画化的组件:View
、Text
、Image
和ScrollView
,不过你也可以使用Animated.createAnimatedComponent()
来封装你自己的组件。
组合动画
动画还可以使用组合函数以复杂的方式进行组合:
-
Animated.delay()
在给定延迟后开始动画。 -
Animated.parallel()
同时启动多个动画。 -
Animated.sequence()
按顺序启动动画,等待每一个动画完成后再开始下一个动画。 -
Animated.stagger()
按照给定的延时间隔,顺序并行的启动动画。
动画也可以通过将toValue
设置为另一个动画的Animated.Value
来简单的链接在一起。
默认情况下,如果一个动画停止或中断,则组中的所有其他动画也会停止。