React Native动画Animated详解
在移动开发中,动画是提高用户体验不可缺少的一个元素。在React Native中,动画API提供了一些现成的组件:Animated.View,Animated.Text和Animated.Image默认支持动画。动画API会调用iOS或者Android的本地代码来完成这些组件的位移、大小等动画。
在React Native中,Animated创建过程如下:
创建Animated.Value,设置初始值,比如一个视图的opacity属性,最开始设置Animated.Value(0),来表示动画的开始时候,视图是全透明的。 AnimatedValue绑定到Style的可动画属性,比如透明度,{opacity: this.state.fadeAnim} 使用Animated.timing来创建自动的动画,或者使用Animated.event来根据手势,触摸,Scroll的动态更新动画的状态(本文会侧重讲解Animated.timing) 调用Animated.timeing.start()开始动画 Animated简介大多数情况下,在 React Native 中创建动画是推荐使用 Animated API 的,其提供了三个主要的方法用于创建动画:
Animated.timing() -- 推动一个值按照一个过渡曲线而随时间变化。Easing 模块定义了很多缓冲曲线函数。 Animated.decay() -- 推动一个值以一个初始的速度和一个衰减系数逐渐变为0。 Animated.spring() -- 产生一个基于 Rebound 和 Origami 实现的Spring动画。它会在toValue 值更新的同时跟踪当前的速度状态,以确保动画连贯。
除了这三个创建动画的方法,对于每个独立的方法都有三种调用该动画的方式:
Animated.parallel() --同时开始一个动画数组里的全部动画。默认情况下,如果有任何一个动画停止了,其余的也会被停止。你可以通过stopTogether 选项来改变这个效果。 Animated.sequence() --按顺序执行一个动画数组里的动画,等待一个完成后再执行下一个。如果当前的动画被中止,后面的动画则不会继续执行。 Animated.stagger() -- 一个动画数组,里面的动画有可能会同时执行(重叠),不过会以指定的延迟来开始。 Animated.timing()使用 Animated.timing 创建的旋转动画。Animated.timing()的基本使用方法如下:
Animated.timing( someValue, toValue: number, duration: number, easing: easingFunction, delay: number )
Easing 也是用React Native创建动画的载体,它允许我们使用已经定义好的各种缓冲函数,例如:linear, ease, quad, cubic, sin, elastic, bounce, back, bezier, in, out, inout 。由于有直线运动,我们将使用 linear。
接下来,需要在构造函数中初始化一个带动画属性的值用于旋转动画的初始值:
constructor () { super() this.spinValue = new Animated.Value(0) }
我们使用 Animated.Value声明了一个 spinValue 变量,并传了一个 0 作为初始值。然后创建了一个名为 spin 的方法,并在 componentDidMount 中调用它,目的是在 app 加载之后运行动画。
componentDidMount () { this.spin() spin () { this.spinValue.setValue(0) Animated.timing( this.spinValue, toValue: 1, duration: 4000, easing: Easing.linear ).start(() = this.spin()) }
现在方法已经创建好了,接下来就是在UI中渲染动画了。
render () { const spin = this.spinValue.interpolate({ inputRange: [0, 1], outputRange: [0deg, 360deg] return ( View https://s3.amazonaws.com/media-p.slid.es/uploads/alexanderfarennikov/images/1198519/reactjs.png}} /View }
实现效果:
完整代码:
/** * Sample React Native App * https://github.com/facebook/react-native * @flow import React, {Component} from react; import { AppRegistry, StyleSheet, Text, Animated, TouchableOpacity, Easing, View } from react-native;Animated.spring()
return ( View https://s3.amazonaws.com/media-p.slid.es/uploads/alexanderfarennikov/images/1198519/reactjs.png}} TouchableOpacity onPress={() = this.spin()} center, alignItems: center, button: { marginTop: 20, backgroundColor:#808080, height:35, width:140, borderRadius:5, justifyContent: center, alignItems: center, export default AnimationRotateScene;
使用 Animated.spring() 方法创建一个放大缩小的动画。
Animated.spring() 方法使用:
Animated.spring( someValue, toValue: number, friction: number )
如上图所示,我们要使用Animated.spring()创建一个放大缩小的动画效果。
在构造函数中,创建一个 springValue 变量,初始化其值为0.3。
constructor () { super() this.springValue = new Animated.Value(0.3)
然后,删除 animated 方法和componentDidMount方法,创建一个新的 spring 方法。
spring () { this.springValue.setValue(0.3) Animated.spring( this.springValue, toValue: 1, friction: 1 ).start() }
然后我们给View的button添加一个点击事件,出发上面的spring动画。
View https://s3.amazonaws.com/media-p.slid.es/uploads/alexanderfarennikov/images/1198519/reactjs.png}}/ /View
完整代码如下:
/** * Sample React Native App * https://github.com/facebook/react-native * @flow import React, {Component} from react; import { AppRegistry, StyleSheet, Text, Animated, TouchableOpacity, Easing, View } from react-native;Animated.parallel()
return ( View https://s3.amazonaws.com/media-p.slid.es/uploads/alexanderfarennikov/images/1198519/reactjs.png}} TouchableOpacity onPress={() = this.spin()} center, alignItems: center, button: { marginTop: 20, backgroundColor:#808080, height:35, width:140, borderRadius:5, justifyContent: center, alignItems: center, export default AnimationRotateScene;
Animated.parallel() 会同时开始一个动画数组里的全部动画。parallel()会接受一个动画数组,首先看一下api:
Animated.parallel(arrayOfAnimations) // In use: Animated.parallel([ Animated.spring( animatedValue, //config options Animated.timing( animatedValue2, //config options ])
所以,我们先创建一个动画数组,并初始化。
constructor () { super() this.animatedValue1 = new Animated.Value(0) this.animatedValue2 = new Animated.Value(0) this.animatedValue3 = new Animated.Value(0) }
然后,创建一个 animate 方法并在 componendDidMount() 中调用它。
componentDidMount () { this.animate() animate () { this.animatedValue1.setValue(0) this.animatedValue2.setValue(0) this.animatedValue3.setValue(0) const createAnimation = function (value, duration, easing, delay = 0) { return Animated.timing( value, toValue: 1, duration, easing, delay Animated.parallel([ createAnimation(this.animatedValue1, 2000, Easing.ease), createAnimation(this.animatedValue2, 1000, Easing.ease, 1000), createAnimation(this.animatedValue3, 1000, Easing.ease, 2000) ]).start() }
在 animate 方法中,我们将三个动画属性值重置为0。此外,还创建了一个 createAnimation 方法,该方法接受四个参数:value, duration, easing, delay(默认值是0),返回一个新的动画。
然后,调用 Animated.parallel(),并将三个使用 createAnimation 创建的动画作为参数传递给它。在 render 方法中,我们需要设置插值:
render () { const scaleText = this.animatedValue1.interpolate({ inputRange: [0, 1], outputRange: [0.5, 2] const spinText = this.animatedValue2.interpolate({ inputRange: [0, 1], outputRange: [0deg, 720deg] const introButton = this.animatedValue3.interpolate({ inputRange: [0, 1], outputRange: [-100, 400] }
最后,我们用一个主 View 包裹三个 Animated.Views:
View absolute}} TouchableHighlight onPress={this.animate.bind(this)} white, fontSize: 20}} Click Here To Start /Text /TouchableHighlight /Animated.View /View
完整的代码如下:
/** * Sample React Native App * https://github.com/facebook/react-native * @flow 组动画 import React, {Component} from react; import { AppRegistry, StyleSheet, Text, Animated, TouchableOpacity, TouchableHighlight, Easing, View } from react-native;示例使用说明
this.animatedValue1 = new Animated.Value(0) this.animatedValue2 = new Animated.Value(0) this.animatedValue3 = new Animated.Value(0) componentDidMount() { this.animate() animate() { this.animatedValue1.setValue(0) this.animatedValue2.setValue(0) this.animatedValue3.setValue(0) const createAnimation = function (value, duration, easing, delay = 0) { return Animated.timing( value, toValue: 1, duration, easing, delay Animated.parallel([ createAnimation(this.animatedValue1, 2000, Easing.ease), createAnimation(this.animatedValue2, 1000, Easing.ease, 1000), createAnimation(this.animatedValue3, 1000, Easing.ease, 2000) ]).start() startAnimation() { this.state.currentAlpha = this.state.currentAlpha == 1.0 ? 0.0 : 1.0; Animated.timing( this.state.fadeAnim, {toValue: this.state.currentAlpha} ).start(); render() { const scaleText = this.animatedValue1.interpolate({ inputRange: [0, 1], outputRange: [0.5, 2] const spinText = this.animatedValue2.interpolate({ inputRange: [0, 1], outputRange: [0deg, 720deg] const introButton = this.animatedValue3.interpolate({ inputRange: [0, 1], outputRange: [-100, 400] return ( View absolute}} TouchableHighlight onPress={this.animate.bind(this)} center, alignItems: center, button: { marginTop: 20, backgroundColor: #808080, height: 35, width: 140, borderRadius: 5, justifyContent: center, alignItems: center, export default AnimationGroupScene;
如图所示,我对动画的代码做了一个简单的整理,大家在使用的时候直接引入AnimationRoot文件即可。
AnimationRoot文件内容如下:
/** * Sample React Native App * https://github.com/facebook/react-native * @flow import React, {Component} from react; import { StackNavigator } from react-navigation;
import AnimationIndex from ./AnimationIndex; import AnimationSpringScene from ./AnimationSpringScene;//缩放动画 import AnimationRotateScene from ./AnimationRotateScene;//旋转动画 import AnimationAlphaScene from ./AnimationAlphaScene;//Alpha动画 import AnimationGroupScene from ./AnimationGroupScene;//组动画 import AnimationFrameScene from ./AnimationFrameScene;//帧动画
AnimationIndex: { screen: AnimationIndex }, AnimationSpringScene: { screen: AnimationSpringScene }, AnimationRotateScene: { screen: AnimationRotateScene }, AnimationAlphaScene: { screen: AnimationAlphaScene }, AnimationGroupScene: { screen: AnimationGroupScene }, AnimationFrameScene: { screen: AnimationFrameScene }, export default anim;
最后是项目实现的最终结果图,代码地址动画源码
code_xzh 对前端移动客户端技术比较擅长。著有《React Native移动开发实战》和《Kotlin入门与实战》和《Weex跨平台实战》,《React Native移动开发进阶》即将出版,正在努力完成《Flutter跨平台开发实战》
相关文章
- [Android Pro] ListView,GridView之LayoutAnimation特殊动画的实现
- IOS项目之弹出动画二
- 使用Timer组件实现人物动画效果
- [React] Using react-styleguidist for Component demo
- [React Intl] Use a react-intl Higher Order Component to format messages
- [React Intl] Render Content Based on a Number using react-intl FormattedMessage (plural)
- [React Intl] Render Content with Markup Using react-intl FormattedHTMLMessage
- 动画 -- 按钮 -- 左右晃动
- 动画 --- 添加 和 移除 按钮
- [React] Ensure all React useEffect Effects Run Synchronously in Tests with react-testing-library
- [React Intl] Use a react-intl Higher Order Component to format messages
- [React Intl] Render Content Based on a Number using react-intl FormattedMessage (plural)
- [React Intl] Format Numbers with Separators and Currency Symbols using react-intl FormattedNumber
- [React Intl] Format Date and Time Using react-intl FormattedDate and FormattedTime
- [React Intl] Render Content with Placeholders using react-intl FormattedMessage
- React的source code init时会自动检测Chrome dev tool的react extension装了没
- Android 动画之属性动画详解
- vue v-for动画bug
- 解决Activity启动黑屏和设置android:windowIsTranslucent不兼容activity切换动画的问题
- Cocos2d-x开发实例介绍帧动画使用
- 203:vue+openlayers 地图旋转移动动画、CSS缩放动画,介绍animate的使用方法
- 186:vue+openlayers 小汽车移动轨迹动画,带开始、暂停、结束控制键
- Taro React组件开发(1) —— Overlay 遮罩层【渐入渐出动画遮罩层】