ReactNative Animations

ReactNative不仅能做到H5那样的页面元素布局,还能做到像Native一样自然的动画。目前它有两套动画系统—AnimatedLayoutAnimationAnimated更注重小而精细的动画控制,LayoutAnimation更关注全局布局类型的动画。

##Animated

先来看个简单的例子,在UIKit中做个简单的动画:

1
2
3
4
[UIView animateWithDuration:0.3 animations:^{
aView.transform = CGAffineTransformMakeScale(1.2, 1.2);
} completion:^(BOOL finished) {
}];

在RN中怎么做这个动画呢?

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
class Playground extends React.Component {
constructor(props: any) {
super(props);
this.state = {
bounceValue: new Animated.Value(0),
};
}
render(): ReactElement {
console.log('rending...');
return (
<Animated.Image // Base: Image, Text, View
source=【【uri: 'http://i.imgur.com/XMKOH81.jpg'】】
style=【【
flex: 1,
transform: [ // `transform` is an ordered array
{scale: this.state.bounceValue}, // Map `bounceValue` to `scale`
]
】】
/>
);
}
componentDidMount() {
this.state.bounceValue.setValue(0.8); // Start large
Animated.timing( // Base: spring, decay, timing
this.state.bounceValue, // Animate `bounceValue`
{
toValue: 1.2, // Animate to smaller size
duration: 2000,
delay: 1000,
}
).start(); // Start the animation
}
}

这段JS代码,有三点需要解释一下:

  1. 为什么render方法这里只执行一次,尽管这个Animated.Imagetransform的值在做变化
  2. Animated.Image这是个什么组件?它和Image有啥关系呢?
  3. bounceValue: new Animated.Value(0)Animated.Value是什么?
  4. 最后用到了native的Core Animation吗?怎么桥接的?

解释:

  1. 这里要回答第一个问题,需要先解释下第二个问题。什么是Animated.Image?

    看Animated.js里的最后module.exports

2

这个Component是通过`createAnimatedComponent`包装的,那么这层包装做了什么呢?
对传给Component的每个含有Animated属性的值做监听。下次`requestAnimationFrame`的时候,就去检测这些值有没有被修改,如果修改了,就用这个组件的setNativeProps来更新UI,这样做是不会重新渲染这个组件的整个DOM tree的,[详情说明](http://facebook.github.io/react-native/docs/direct-manipulation.html#content)。
  1. 回答第一个问题,transfrom的值变化是内部组件由requestAnimationFrame而导致的,而不是传统的改变state或者props来驱动的。
  2. 这个Animated.Value(0)是与Animated.Component约定好的一个属性类,当这个值按照动画曲线变化时,Animated.Component会追踪这个值,并更新自己那些用到Animated.Value的UI 属性,从而达到动画的效果。
  3. RN Animated系统没有用到native的动画系统。是自己用JS实现了一套动画系统,可能为了平台兼容性。

总结: 整个过程如图所示

3

##LayoutAnimation

先看看用法:

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
var App = React.createClass({
componentWillMount() {
// Animate creation
LayoutAnimation.spring();
},
getInitialState() {
return { w: 100, h: 100 }
},
_onPress() {
// Animate the update
LayoutAnimation.spring();
this.setState({w: this.state.w + 15, h: this.state.h + 15})
},
render: function() {
return (
<View style={styles.container}>
<View style={[styles.box, {width: this.state.w, height: this.state.h}]} />
<TouchableOpacity onPress={this._onPress}>
<View style={styles.button}>
<Text style={styles.buttonText}>Press me!</Text>
</View>
</TouchableOpacity>
</View>
);
}
});

可以看到LayoutAnimation针对的是某个React的component,当component重新渲染时,它会用到这个animation。

那么它是如何实现的呢?

源码中可以看到它和Native bridge过来的RCTUIManager里用到。在看RCTUIManagerLayoutAnimation的时候,它是封装系统的Core Animation API.

4

总结:LayoutAnimation是基于Native的animation,只是对单个component渲染到屏幕或者重新渲染时用到,但它的可控的细粒度没有Animated高。