【译】详解React Native动画
大多数情况下,在 React Native 中创建动画是推荐使用 Animated API 的,其提供了三个主要的方法用于创建动画:
以我的经验来看,Animated.timing() 和 Animated.spring() 在创建动画方面是非常有效的。 除了这三个创建动画的方法,对于每个独立的方法都有三种调用该动画的方式:
1. Animated.timing()
第一个要创建的动画是使用 // Example implementation: Animated.timing( someValue,{ toValue: number,duration: number,easing: easingFunction,delay: number } ) 这种方式常用于创建需要loading指示的动画,在我使用React Native的项目中,这也是创建动画最有效的方式。这个理念也可以用于其它诸如按比例放大和缩小类型的指示动画。 开始之前,我们需要创建一个新的React Native 项目或者一个空的React Native项目。创建新项目之前,需要输入 react-native init animations cd animations 然后打开 现在已经创建了一个新项目,则第一件事是在已经引入的 View 之后从 import { AppRegistry,StyleSheet,Text,View,Animated,Image,Easing } from 'react-native' Animated 是我们将用于创建动画的库,和React Native交互的载体。 Image 用于在UI中显示图片。 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 作为初始值。 然后创建了一个名为 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 style={styles.container}> <Animated.Image style={{ width: 227,height: 200,transform: [{rotate: spin}] }} source={{uri: 'https://s3.amazonaws.com/media-p.slid.es/uploads/alexanderfarennikov/images/1198519/reactjs.png'}} /> </View> ) }
transform: [{rotate: spin}] 最后,在 const styles = StyleSheet.create({ container: { flex: 1,justifyContent: 'center',alignItems: 'center' } }) 这个示例动画的最终代码在这里。 关于Easing这是 我创建了另外一个示例项目,里面包含了大部分 easing 动画的实现,可以供你参考,链接在这里。(项目的运行截图)依据在下面:
该项目实现的 easing 动画在 RNPlay 的地址在这里。 2. Animated.timing 示例
上文已经说过了 Animated.timing 的基础知识,这一节会例举更多使用 Animated.timing 与 interpolate 结合实现的动画示例。 下一个示例中,会声明一个单一的动画值,
在开始之前,可以创建一个新分支或者清除上一个项目的旧代码。 第一件事是在构造函数中初始化一个需要用到的动画属性值: constructor () { super() this.animatedValue = new Animated.Value(0) } 接下来,创建一个名为 componentDidMount () { this.animate() } animate () { this.animatedValue.setValue(0) Animated.timing( this.animatedValue,duration: 2000,easing: Easing.linear } ).start(() => this.animate()) } 在 render () { const marginLeft = this.animatedValue.interpolate({ inputRange: [0,outputRange: [0,300] }) const opacity = this.animatedValue.interpolate({ inputRange: [0,0.5,1,0] }) const movingMargin = this.animatedValue.interpolate({ inputRange: [0,300,0] }) const textSize = this.animatedValue.interpolate({ inputRange: [0,outputRange: [18,32,18] }) const rotateX = this.animatedValue.interpolate({ inputRange: [0,'180deg','0deg'] }) ... } interpolate 是一个很强大的方法,允许我们用多种方式来使用单一的动画属性值:this.animatedValue。因为 最后,返回实现了上述变量的 Animated.View 和 Animated.Text 组件: return ( <View style={styles.container}> <Animated.View style={{ marginLeft,height: 30,width: 40,backgroundColor: 'red'}} /> <Animated.View style={{ opacity,marginTop: 10,backgroundColor: 'blue'}} /> <Animated.View style={{ marginLeft: movingMargin,backgroundColor: 'orange'}} /> <Animated.Text style={{ fontSize: textSize,color: 'green'}} > Animated Text! </Animated.Text> <Animated.View style={{ transform: [{rotateX}],marginTop: 50,backgroundColor: 'black'}}> <Text style={{color: 'white'}}>Hello from TransformX</Text> </Animated.View> </View> ) 当然,也需要更新下 const styles = StyleSheet.create({ container: { flex: 1,paddingTop: 150 } }) 这个示例动画的最终代码在这里。 3. Animated.spring()
接下来,我们将会使用 Animated.spring() 方法创建动画。 // Example implementation: Animated.spring( someValue,{ toValue: number,friction: number } ) 我们继续使用上一个项目,并只需要更新少量代码就行。在构造函数中,创建一个 springValue 变量,初始化其值为0.3: constructor () { super() this.springValue = new Animated.Value(0.3) } 然后,删除 spring () { this.springValue.setValue(0.3) Animated.spring( this.springValue,friction: 1 } ).start() }
动画已经设置好了,我们将其放在 View 的click事件中,动画元素依然是之前使用过的 React logo 图片: <View style={styles.container}> <Text style={{marginBottom: 100}} onPress={this.spring.bind(this)}>Spring</Text> <Animated.Image style={{ width: 227,transform: [{scale: this.springValue}] }} source={{uri: 'https://s3.amazonaws.com/media-p.slid.es/uploads/alexanderfarennikov/images/1198519/reactjs.png'}}/> </View>
这个示例动画的最终代码在这里。 4. Animated.parallel()
Animated.parallel() 会同时开始一个动画数组里的全部动画。 先看一下这个api是怎么调用的: // 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) } 然后,创建一个 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,delay } ) } Animated.parallel([ createAnimation(this.animatedValue1,2000,Easing.ease),createAnimation(this.animatedValue2,1000,Easing.ease,1000),createAnimation(this.animatedValue3,2000) ]).start() } 在 然后,调用 在 render () { const scaleText = this.animatedValue1.interpolate({ inputRange: [0,outputRange: [0.5,2] }) const spinText = this.animatedValue2.interpolate({ inputRange: [0,'720deg'] }) const introButton = this.animatedValue3.interpolate({ inputRange: [0,outputRange: [-100,400] }) ... }
最后,我们用一个主 View 包裹三个 Animated.Views: <View style={[styles.container]}> <Animated.View style={{ transform: [{scale: scaleText}] }}> <Text>Welcome</Text> </Animated.View> <Animated.View style={{ marginTop: 20,transform: [{rotate: spinText}] }}> <Text style={{fontSize: 20}}> to the App! </Text> </Animated.View> <Animated.View style={{top: introButton,position: 'absolute'}}> <TouchableHighlight onPress={this.animate.bind(this)} style={styles.button}> <Text style={{color: 'white',fontSize: 20}}> Click Here To Start </Text> </TouchableHighlight> </Animated.View> </View> 当 这个示例动画的最终代码在这里。 5. Animated.Sequence()
先看一下这个api是怎么调用的: // API Animated.sequence(arrayOfAnimations) // In use Animated.sequence([ Animated.timing( animatedValue,Animated.spring( animatedValue2,{ //config options } ) ]) 和 Animated.parallel() 一样, Animated.sequence() 接受一个动画数组。但不同的是,Animated.sequence() 是按顺序执行一个动画数组里的动画,等待一个完成后再执行下一个。 import React,{ Component } from 'react'; import { AppRegistry,Animated } from 'react-native' const arr = [] for (var i = 0; i < 500; i++) { arr.push(i) } class animations extends Component { constructor () { super() this.animatedValue = [] arr.forEach((value) => { this.animatedValue[value] = new Animated.Value(0) }) } componentDidMount () { this.animate() } animate () { const animations = arr.map((item) => { return Animated.timing( this.animatedValue[item],{ toValue: 1,duration: 50 } ) }) Animated.sequence(animations).start() } render () { const animations = arr.map((a,i) => { return <Animated.View key={i} style={{opacity: this.animatedValue[a],height: 20,width: 20,backgroundColor: 'red',marginLeft: 3,marginTop: 3}} /> }) return ( <View style={styles.container}> {animations} </View> ) } } const styles = StyleSheet.create({ container: { flex: 1,flexDirection: 'row',flexWrap: 'wrap' } }) AppRegistry.registerComponent('animations',() => animations); 由于 这个示例动画的最终代码在这里。 6. Animated.Stagger()(图片太大,上传不了。gif动态图) 先看一下这个api是怎么调用的: // API Animated.stagger(delay,arrayOfAnimations) // In use: Animated.stagger(1000,[ Animated.timing( animatedValue,{ //config options } ) ]) 和 Animated.parallel() 和 Animated.sequence() 一样, Animated.Stagger 接受一个动画数组。但不同的是,Animated.Stagger 里面的动画有可能会同时执行(重叠),不过会以指定的延迟来开始。 与上述两个动画主要的不同点是 Animated.Stagger 的第一个参数, import React,Animated } from 'react-native' const arr = [] for (var i = 0; i < 500; i++) { arr.push(i) } class animations extends Component { constructor () { super() this.animatedValue = [] arr.forEach((value) => { this.animatedValue[value] = new Animated.Value(0) }) } componentDidMount () { this.animate() } animate () { const animations = arr.map((item) => { return Animated.timing( this.animatedValue[item],duration: 4000 } ) }) Animated.stagger(10,animations).start() } render () { const animations = arr.map((a,flexWrap: 'wrap' } }) AppRegistry.registerComponent('SampleApp',() => animations); 这个示例动画的最终代码在这里。
原文地址https://github.com/dwqs/blog/... (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |