React Native Animations

React Native Animation

目录

React Native动画实现的方式

React Native本身提供的动画实现有如下两种方式:

  1. LayoutAnimation
    主要针对全局的布局变化处理的动画效果

    1
    2
    优点:使用简单
    缺点:实现精细的动画会比较麻烦
  2. Animated API
    主要是用于创建精细的交互动画

    1
    2
    优点:用针对指定值进行动态变化,以达到实现精细的交互动画控制,并且性能高
    缺点:使用上较LayoutAnimation麻烦,并且需要动画控制的Component只能是Animated中封装的View等四个可动画化组件,但也支持自定义创建一个可动画化的组件

LayoutAnimation

这里会以简单例子说明一下LayoutAnimation的使用

首先,如果是Android平台,需要先在加上下面代码设置打开动画,这样LayoutAnimation才能正常运作

1
2
> UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);
>

从根本使用上看,实际上LayoutAnimation的API使用都是设置一个动画,然后再下一次布局变化的时候生效,因此,实际上LayoutAnimation的触发条件除了配置动画外,还需要布局发生变化,如果一个Component知识单纯地改变样式,而没有影响到布局的更新,这个时候是不会触发动画效果的。

LayoutAnimation有以下两种种API的使用方式:

  1. 最为简单的直接使用预设的,预设的动画函数有以下三种:
1
2
3
4
> easeInEaseOut()
> linear()
> spring()
>

使用上有两种方式:

第一种:直接在componentWillUpdate()中设置

1
2
3
4
> componentWillUpdate() {
> LayoutAnimation.easeInEaseOut()
> }
>

第二种:在设置state的时候同时设置

1
2
3
4
5
> this.setState({
> ...
> })
> LayoutAnimation.easeInEaseOut()
>
  1. 使用参数配置自定义的方式
1
2
3
4
5
6
7
8
9
10
11
12
13
> LayoutAnimation.configureNext({
> duration: 700,
> create: {
> type: LayoutAnimation.Types.easeInEaseOut,
> },
> update: {
> type: LayoutAnimation.Types.easeInEaseOut,
> },
> delete: {
> type: LayoutAnimation.Types.easeInEaseOut,
> },
> })
>

具体使用方式与上面预设的方式一致

Animated

相较于LayoutAnimationAnimated在使用上就显得相对复杂一点,但实际上也是非常的便利,而且可配置性比较高。

Animated的核心就是通过使用Animated.Value这个类,创建Value对象,绑定到组件上,然后通过动画时间函数来驱动这个Value里面的值变化。

但这里绑定的组件并不是随意一个组件都可以,绑定的组件是需要可动画化的组件,这些组件能通过与Animated.Value绑定到属性,然后直接在Native层进行更新,以避免直接在js层的渲染处理所造成的开销。在React Native的Animated里面,已经提供了以下几种可动画化组件:

  • Animated.Image
  • Animated.ScrollView
  • Animated.Text
  • Animated.View

除了Animated里面的这四个可动画化组件,还可以通过使用

  • createAnimatedComponent()

来创建一个自定义的可动画化组件

以下为一个简单例子,实现一个渐隐动画。

首先,我们需要new一个Animated.Value对象:

1
2
3
4
> this.state = {
> opacity: new Animated.Value(1)
> }
>

然后,将该对象绑定到一个可动画化的组件上

1
2
> <Animated.View style={{width: 100, height: 100, backgroundColor: 'red', opacity: this.state.opacity}} />
>

最后,通过Animated.timing()来创建一个CompositeAnimation对象,最后调用CompositeAnimation对象的start()方法来开始动画

1
2
3
4
5
> Animated.timing(this.state.opacity, {
> toValue: 0,
> duration: 500,
> }).start()
>

以上就是一个简单的渐隐的动画实现了,其中timing()函数第二个参数是动画的配置,这里只配置了变化最终的目标值,以及动画持续时间,实际上还可以设定动画的曲线函数

1
2
> easing: Easing.inOut(Easing.ease)
>

然而在实际开发过程中,我们需要复杂一点的组合动画的时候,要如何实现?通过分解动画,我们可以了解到常见的一些稍微复杂的交互动画实际上是多个Component的一个或多个样式属性同时变化。

Animated中提供了以下几种组合函数来实现复杂的动画组合:

  • Animated.delay()
  • Animated.parallel()
  • Animated.sequence()
  • Animated.stagger()

这里以其中的parallel()方法来举个例子来说明这些函数的使用方式

1
2
3
4
5
> var parallel = function(
> animations: Array<CompositeAnimation>,
> config?: ?ParallelConfig,
> ): CompositeAnimation
>

通过查看源码可以看到,parallel()方法必要参数其实就是CompositeAnimation对象的数组,返回也是CompositeAnimation对象,也就是把传入的多个CompositeAnimation组合为一个,因此这几个组合函数实际上也是可以嵌套调用的,整个自由度是比较高的。

下面例子实现两个View分别向上下不同方向的移动效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class AnimatedTranslateDemo extends Component {
constructor(props) {
super(props)
this.state = {
toggled: true,
top1: new Animated.Value(100),
top2: new Animated.Value(300),
}
}
startAnimation() {
if (this.state.toggled) {
let animations = ['top1', 'top2'].map(property => {
let toValue = property === 'top2' ? 100 : 300
return Animated.timing(this.state[property], {
toValue:toValue,
duration: 500,
})
})
Animated.parallel(animations).start()
} else {
let animations = ['top1', 'top2'].map(property => {
let toValue = property === 'top2' ? 300 : 100
return Animated.timing(this.state[property], {
toValue:toValue,
duration: 500,
})
})
Animated.parallel(animations).start()
}
this.setState({toggled: !this.state.toggled})
}
render() {
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Animated.View style={{position: 'absolute', top: this.state.top1, left: 50, width: 50, height: 50, backgroundColor: 'red'}} />
<Animated.View style={{position: 'absolute', top: this.state.top2, right: 50, width: 50, height: 50, backgroundColor: 'blue'}} />
<Button style={{width: 100, height:50}} onPress={this.startAnimation.bind(this)} title="start" />
</View>
)
}
}

至此,以上就是React Native的动画实现的简单介绍,下面链接为Demo项目的地址
Animations

参考资料

React Native官方文档

React Native Animated源码

React Native LayoutAnimation源码