React Native 中实现动画的主要框架就是 Animated
, 经过很多版本的迭代,Animated 已经相当完善,基本能满足日常开发需求了。
关于 Animated 的资料,文档网上有很多了,这里不展开来详细讲。总结一下自己开发这段时间来对 Animated 的小理解。
Animated VS 原生动画
苹果对自家平台 App 的用户体验非常重视,可以说现在 RN 平台的动画库,完全比不上 iOS SDK 所能提供的效果,充其量只能算是一个精华简化版本。而且苹果每年的 WWDC 对自家动画库的更新都十分重视。
在一些 Animated 组件中可以打开 useNative
开关,从而借助 iOS 平台底层的动画库实现效果,以得到更加流畅的效果。
相比于原生动画的实现方式,Animated:
- 目前只支持
Animated.Text
,Animated.Image
,Animated.View
等几个指定的组件,自定义的动画组件实现比较麻烦。 - 必须使用
Animated Value
来作为动画的驱动,而不是被动画组件的具体状态,不直观。 - 整个动画的代码被割裂,导致回溯阅读修改麻烦。
Animated VS LayoutAnimated
LayoutAnimated
是基于组件 Frame 的简化版数据库,非常类似于 Cocoa 中的 UIView animate block。而 Animated 是基于 Value 驱动的,要复杂的多。
在一些场景下,LayoutAnimated 要比 Animated 更为直接简单。
LayoutAnimated 简单实现:
startAnimation() {
LayoutAnimation.configureNext({
duration: 700, //持续时间
create: { // 视图创建
type: LayoutAnimation.Types.spring,
property: LayoutAnimation.Properties.scaleXY,// opacity、scaleXY
},
update: { // 视图更新
type: LayoutAnimation.Types.spring,
},
});
this.setState({width: this.state.width + 10, height: this.state.height + 10});
}
Animated 核心逻辑
总结为两个词:绑定和驱动。
绑定,就是将组件的属性和 Animated.Value 绑定,value 值变化驱动组件重绘形成动画。这和原生动画中使用组件本身的属性来驱动动画是有区分的。
驱动,就是控制 Animated.Value 的值在一段时间内的函数变化,可以使用现成的曲线(etc: Easing),也可以绑定到其它 value 值上形成映射(Animated.Event),Animated 的很多功能,本质上就是对于 Animated.Value 的不同驱动方式。
也贴一点代码,一个简单的 Animated 实现:
constructor(props) {
super(props);
this.state = {
// 定义一个 Animated.Value 值
bgOpacity: new Animated.Value(0),
};
this.bgOpacityToValue = 1;
}
componentDidMount() {
let animates = [
// 使用 Animated 库函数驱动这个 Value 值变化,形成动画
Animated.spring(this.state.bgOpacity, {
toValue: this.bgOpacityToValue,
})
];
this.state.bgOpacity.setValue(0);
Animated.parallel(animates).start();
}
render() {
return (
<View style={styles.screen} pointerEvents='box-none'>
// 定义 Animated.View, 并且将 Animated.Value 绑定到它的一个属性上
<Animated.View
style={[styles.screen, styles.screenFlexStyle, {backgroundColor: 'rgba(0, 0, 0, 0.3)', opacity: this.state.bgOpacity}]}>
</View>
</Animated.View>
</View>
);
}
其它 Animated 的高级实现,比如映射、绑定、组合、关键帧、动画曲线等等,可以参考文档。
Animated Event
起初对 Animated.Event
绑定非常疑惑,也算是 Animated 中比较难理解的一个点,官方的示例代码如下:
onScroll={Animated.event(
[{nativeEvent: {contentOffset: {x: this._scrollX}}}],
{listener: (event) => console.log(event)}, // Optional async listener
)}
文档本身对此没有太多解释。这一段中,onScroll 是配置 ScrollView 的回调函数,所以 Animated.event() 其实返回的是一个方法,它将会被 scroll 调用,传入当前的滚动事件 e。
Animated.event() 的第一个参数是绑定序列:规定了如何将 Animated.Value() 绑定到指定的源上,可以看做是一种特殊的语法,用伪代码写就是:
e.nativeEvent.contentOffset.x 绑定到 this._scrollX
第二个参数是 config,这里指定了事件的监听函数,其实就是 onScroll 的正常监听函数,其它可选的配置见文档。
所以可以翻译一下这段拗口的语法,等于:
onScroll={(e) => {
Animated.event(
[{nativeEvent: {contentOffset: {x: this._scrollX}}}],
{listener: (event) => console.log(event)}, // Optional async listener
)(e);
}}