另外,我在Github上创建了一个仓库来搜集优秀的React Native库和优秀的博客等html
ReactNativeMaterialsreact
目前,React Native的版本是0.28,主要的动画分为两大类git
目前React native的release速度仍是比较快的,每隔2周左右就release一次。github
本文默认读者已经spring
react-native init Demo --verbose
初始化了一个Demo项目一个最基本的Animated建立过程以下react-native
Animated.Value
,设置初始值,好比一个视图的opacity
属性,最开始设置Animated.Value(0)
,来表示动画的开始时候,视图是全透明的。Animated.timing
来建立自动的动画,或者使用Animated.event
来根据手势,触摸,Scroll的动态更新动画的状态(本文会侧重讲解Animated.timing
)Animated.timeing.start()
开始动画基于上述的原理,咱们来实现第一个动画。数组
建立一个Demo工程的时候,运行后,模拟器截图应该是酱紫的。app
而后,只保留第一行文字,而后咱们给这个默认的视图建立fade in动画,效果以下异步
代码ide
class Demo extends React.Component { state: { //能够不写,我这里写是为了去除flow警告 fadeAnim: Object, }; constructor(props) { super(props); this.state = { fadeAnim: new Animated.Value(0), //设置初始值 }; } componentDidMount() { Animated.timing( this.state.fadeAnim,//初始值 {toValue: 1}//结束值 ).start();//开始 } render() { return ( <View style={styles.container}> <Animated.Text style={{opacity: this.state.fadeAnim}}>//绑定到属性 Welcome to React Native! </Animated.Text> </View> ); } }
因此说,简单的动画就是用Animated.Value
指定初始值,而后在Animated.timing
中设置结束值,其余的交给React native让它自动建立,咱们只须要调用start
开始动画便可。
在当前版本0.27种,可动画的视图包括
static decay(value, config)
阻尼,将一个值根据阻尼系数动画到 0static timing(value, config
根据时间函数来处理,常见的好比线性,加速开始减速结束等等,支持自定义时间函数static spring(value, config)
弹性动画static add(a, b)
将两个Animated.value
相加,返回一个新的static multiply(a, b)
将两个Animated.value
相乘,返回一个新的static modulo(a, modulus)
,将a对modulus取余,相似操做符%static delay(time)
延迟一段时间static sequence(animations)
依次开始一组动画,后一个在前一个结束后才会开始,若是其中一个动画中途中止,则整个动画组中止static parallel(animations, config?)
,同时开始一组动画,默认一个动画中途中止,则全都中止。能够经过设置stopTogether
来重写这一特性static stagger(time, animations)
,一组动画能够同时执行,可是会按照延迟依次开始static event(argMapping, config?)
,利用手势,Scroll来手动控制动画的状态static createAnimatedComponent(Component)
,自定义的让某一个Component支持动画Value
,类型是AnimatedValue
,驱动基本动画AnimatedValueXY
,类型是AnimatedValueXY
,驱动二维动画一个AnimatedValue一次能够驱动多个可动画属性,可是一个AnimatedValue一次只能由一个机制驱动。好比,一个Value能够同时动画View的透明度和位置,可是一个Value一次只能采用线性时间函数
constructor(value)
构造器setValue(value)
直接设置值,会致使动画终止setOffset(offset)
设置当前的偏移量flattenOffset()
将偏移量合并到最初值中,并把偏移量设为0,addListener(callback) ,removeListener(id),removeAllListeners()
,增长一个异步的动画监听者stopAnimation(callback?)
终止动画,并在动画结束后执行callbackinterpolate(config)
插值,在更新可动画属性前用插值函数对当前值进行变换animate(animation, callback)
一般在React Native内部使用stopTracking(),track(tracking)
一般在React Native内部使用和AnimatedValue相似,用在二维动画,使用起来和AnimatedValue相似,这里不在介绍,这里是文档。
有了上文的知识支撑,咱们能够设计并实现一个更加复杂的动画了。
效果
代码(省略了import和style)
class Demo extends React.Component { state: { fadeAnim: Animated, currentAlpha:number, }; constructor(props) { super(props); this.state = {//设置初值 currentAlpha: 1.0,//标志位,记录当前value fadeAnim: new Animated.Value(1.0) }; } startAnimation(){ this.state.currentAlpha = this.state.currentAlpha == 1.0?0.0:1.0; Animated.timing( this.state.fadeAnim, {toValue: this.state.currentAlpha} ).start(); } render() { return ( <View style={styles.container}> <Animated.Text style={{opacity: this.state.fadeAnim, //透明度动画 transform: [//transform动画 { translateY: this.state.fadeAnim.interpolate({ inputRange: [0, 1], outputRange: [60, 0] //线性插值,0对应60,0.6对应30,1对应0 }), }, { scale:this.state.fadeAnim }, ], }}> Welcome to React Native! </Animated.Text> <TouchableOpacity onPress = {()=> this.startAnimation()} style={styles.button}> <Text>Start Animation</Text> </TouchableOpacity> </View> ); } }
经过上文的讲解,相信读者已经对如何用Animated建立动画有了最基本的认识。而有些时候,咱们须要根据Scroll或者手势来手动的控制动画的过程。这就是我接下来要讲的。
手动控制动画的核心是Animated.event
,
这里的Aniamted.event的输入是一个数组,用来作数据绑定
好比,
ScrollView中
onScroll={Animated.event( [{nativeEvent: {contentOffset: {x: this.state.xOffset}}}]//把contentOffset.x绑定给this.state.xOffset )}
Pan手势中
onPanResponderMove: Animated.event([ null,//忽略native event {dx: this.state.pan.x, dy: this.state.pan.y},//dx,dy分别绑定this.state.pan.x和this.state.pan.y ])
目标效果 - 随着ScrollView的相左滑动,最左边的一个Image透明度逐渐下降为0
核心代码
var deviceHeight = require('Dimensions').get('window').height; var deviceWidth = require('Dimensions').get('window').width; class Demo extends React.Component { state: { xOffset: Animated, }; constructor(props) { super(props); this.state = { xOffset: new Animated.Value(1.0) }; } render() { return ( <View style={styles.container}> <ScrollView horizontal={true} //水平滑动 showsHorizontalScrollIndicator={false} style={{width:deviceWidth,height:deviceHeight}}//设置大小 onScroll={Animated.event( [{nativeEvent: {contentOffset: {x: this.state.xOffset}}}]//把contentOffset.x绑定给this.state.xOffset )} scrollEventThrottle={100}//onScroll回调间隔 > <Animated.Image source={require('./s1.jpg')} style={{height:deviceHeight, width:deviceWidth, opacity:this.state.xOffset.interpolate({//映射到0.0,1.0之间 inputRange: [0,375], outputRange: [1.0, 0.0] }),}} resizeMode="cover" /> <Image source={require('./s2.jpg')} style={{height:deviceHeight, width:deviceWidth}} resizeMode="cover" /> <Image source={require('./s3.jpg')} style={{height:deviceHeight, width:deviceWidth}} resizeMode="cover" /> </ScrollView> </View> ); } }
React Native最经常使用的手势就是PanResponser,
因为本文侧重讲解动画,因此不会特别详细的介绍PanResponser,仅仅介绍用到的几个属性和回调方法
onStartShouldSetPanResponder: (event, gestureState) => {}//是否相应pan手势 onPanResponderMove: (event, gestureState) => {}//在pan移动的时候进行的回调 onPanResponderRelease: (event, gestureState) => {}//手离开屏幕 onPanResponderTerminate: (event, gestureState) => {}//手势中断
其中,
目标效果- View随着手拖动而移动,手指离开会到原点
核心代码
class Demo extends React.Component { state:{ trans:AnimatedValueXY, } _panResponder:PanResponder; constructor(props) { super(props); this.state = { trans: new Animated.ValueXY(), }; this._panResponder = PanResponder.create({ onStartShouldSetPanResponder: () => true, //响应手势 onPanResponderMove: Animated.event( [null, {dx: this.state.trans.x, dy:this.state.trans.y}] // 绑定动画值 ), onPanResponderRelease: ()=>{//手松开,回到原始位置 Animated.spring(this.state.trans,{toValue: {x: 0, y: 0}} ).start(); }, onPanResponderTerminate:()=>{//手势中断,回到原始位置 Animated.spring(this.state.trans,{toValue: {x: 0, y: 0}} ).start(); }, }); } render() { return ( <View style={styles.container}> <Animated.View style={{width:100, height:100, borderRadius:50, backgroundColor:'red', transform:[ {translateY:this.state.trans.y}, {translateX:this.state.trans.x}, ], }} {...this._panResponder.panHandlers} > </Animated.View> </View> ); } }
LayoutAnimation在View由一个位置变化到另外一个位置的时候,在下一个Layout周期自动建立动画。一般在setState前掉用LayoutAnimation.configureNext
代码
class Demo extends React.Component { state: { marginBottom:number, }; constructor(props) { super(props); this.state = {//设置初值 marginBottom:0 }; } _textUp(){ LayoutAnimation.spring(); this.setState({marginBottom:this.state.marginBottom + 100}) } render() { return ( <View style={styles.container}> <TouchableOpacity onPress = {()=>this._textUp()} style={{ width:120, height:40, alignItems:'center', marginBottom:this.state.marginBottom, justifyContent:'center', backgroundColor:'#00ffff', borderRadius:20}}> <Text>Text UP</Text> </TouchableOpacity> </View> ); } }
其实代码里只是调用了这一行LayoutAnimation.spring();
,布局修改的时候就显得不那么生硬了
//配置下一次切换的效果,其中config可配置的包括duration(时间),create(配置新的View),update(配置更新的View) static configureNext(config, onAnimationDidEnd?) //configureNext的方便方法 static create(duration, type, creationProp) #
对应三种时间函数
easeInEaseOut: CallExpression # linear: CallExpression # spring: CallExpression #
咱们先建立一个默认的Navigator转场Demo
回拉的时候,前一个时图的移动距离要小于后一个视图
这时候的核心代码以下,MainScreen和DetailScreen就是带一个Button的视图
Navigator的默认的转场动画的实现均可以在这里找到NavigatorSceneConfigs.js。
So,咱们有两种方式来实现自定义的转场动画
篇幅限制,本文只修改默认的转场
好比,我想把默认的PushFromRight动画中,第一个视图的移动距离改成全屏幕。
var ToTheLeftCustom = { transformTranslate: { from: {x: 0, y: 0, z: 0}, to: {x: -SCREEN_WIDTH, y: 0, z: 0},//修改这一行 min: 0, max: 1, type: 'linear', extrapolate: true, round: PixelRatio.get(), }, opacity: { value: 1.0, type: 'constant', }, }; var baseInterpolators = Navigator.SceneConfigs.PushFromRight.animationInterpolators; var customInterpolators = Object.assign({}, baseInterpolators, { out: buildStyleInterpolator(ToTheLeftCustom), }); var baseConfig = Navigator.SceneConfigs.PushFromRight; var CustomPushfromRight = Object.assign({}, baseConfig, { animationInterpolators: customInterpolators, });
而后,修改Navigator的configScene
configureScene={(route, routeStack) => baseConfig}
这时候的动画以下
http://blog.csdn.net/hello_hwc/article/details/51775696