动画对于一款APP的重要性,我想不用多说,想必不是搞开发的也明白,虽说APP的简洁实用性很重要,但UE也是同等重要的。http://blog.csdn.net/jj120522/article/details/52164714
下面分析下网易云音乐的启动动画,一张开启图片缩放的同时首页也进行缩放,不过首页初始化的scale可能是1.5,总是初始化放大,然后缩放至正常状态,造成一种视觉冲击的效果,如果无法感受到的话,还是下载一枚用用。其实这种效果如果用原生开发的话,很简单,Activity之间跳转的动画配置下就完事了,这里简单讲下如何用React Native来实现跨平台的效果。
Animated是一个动画库,用来创造流畅、强大、并且易于构建和维护的动画。
最简单的工作流程就是创建一个Animated.Value,把它绑定到组件的一个或多个样式属性上。然后可以通过动画驱动它,譬如Animated.timing,或者通过Animated.event把它关联到一个手势上,譬如拖动或者滑动操作。除了样式,Animated.value还可以绑定到props上,并且一样可以被插值。
用到的方法:
static timing(value: AnimatedValue | AnimatedValueXY, config: TimingAnimationConfig)
推动一个值按照一个过渡曲线而随时间变化。Easing模块定义了一大堆曲线,你也可以使用你自己的函数。
addListener(callback: ValueXYListenerCallback)
监测动画的变化,因为它并没有提供像原生那么全的方法:如:onAnimationStart,onAnimationEnd等,因此只能不停的进行检测。
这里我们创建个splash组件,一般会在其constructor中进行声明:
[javascript] view plain copy constructor(props){ super(props); //初始化两个变量,一个用于操作scale,一个用于操作opacity. this.state={ bounceAnimValue:new Animated.Value(1), opacityAnimValue:new Animated.Value(1), }; } 然后在componentDidMount中进行启动:
[javascript] view plain copy componentDidMount(){ //第一参数是要修改的变量,第二个是配置config, Animated.timing( this.state.bounceAnimValue, { toValue: 0.8, duration: 400, delay:1000, easing:Easing.linear, } ).start(); Animated.timing( this.state.opacityAnimValue, { toValue:0, duration:400, delay:1000, easing:Easing.linear, } ).start(); this.state.bounceAnimValue.addListener(value=>{ if(value.value=='0.8'){ this.props.onAnimEnd(); } }); }
代码相对还算清晰,easing其实类似于安卓中的Interpolator,可以设置多种形式。
这里用到addListener进行检测动画结束后进行回调给主页面。
下面我们来看下如何render:
[javascript] view plain copy render(){ return( <View style={[styles.container,this.props.style]}> <Animated.Image source={require('./images/splash.jpg')} style={{ flex: 1, width: Util.size.width, height:null, opacity: this.state.opacityAnimValue, transform: [ {scale: this.state.bounceAnimValue}, ], }} /> </View> ); }
注意:只有声明为可动画化的组件才能被关联动画。View、Text,还有Image都是可动画化的。
如果你想让自定义组件可动画化,可以用createAnimatedComponent。如:
[javascript] view plain copy const AnimatedIcon = Animated.createAnimatedComponent(Icon); 。。。 <AnimatedIcon size={60} style={[styles.twitter,{transform:[{scale:this.state.transformAnim}]}]} name="social-twitter"/>
以上是针对splash启动页的操作,同理我们也要对首页进行相应的动画,下面主要看下首页的render函数:
[javascript] view plain copy render(){ let defaultName='app'; let defaultComponent=App; return ( <View style={styles.container}> <View style={styles.main}> <Animated.View style={{ flex:1, transform: [ {scale: this.state.bounceAnimValue}, ] }}> <Navigator initialRoute={{name:defaultName,component:defaultComponent}} configureScene={()=>Navigator.SceneConfigs.PushFromRight} renderScene={(route, navigator) => { let Component = route.component; return <Component {...route.params} navigator={navigator} /> }} /> </Animated.View> </View> {this.state.splashed? null: <Splash style={{height:Util.size.height,position:'absolute'}} onAnimEnd={this.onAnimEnd} /> } </View> ); }
style:
[javascript] view plain copy var styles = StyleSheet.create({ container: { flex: 1, backgroundColor:'#fff' }, main:{ height:Platform.OS==='android'?Util.size.height-24:Util.size.height, width:Util.size.width, position:'absolute', } });
看上去不难理解,将我们的Animated.View包裹住首页内容实现其整体动画效果。但这里有个需要注意的地方,由于splash组件和首页的Navigator组件是属于同一个页面内部,其实也就是splash组件在navigator组件的上方,但React Native并没有原生那么多种布局(直接用RelativeLayout包裹两个控件就成了),但在React Native中,想要实现其效果,需要用到absolute布局并且 设置宽和高。
如图:红色:是最外层的组件,蓝色和黑色要想处于前后效果,需要都设置:
{position:'absolute',width:300,height:500}固定大小,选用absolute布局这样就实现了这种形式。
至此我们就写完了,下面我们看下效果吧
源码链接