React Native开发之动画(Animations)
博主这个系列的文章
另外,我在Github上建立了一个仓库来搜集优秀的React Native库和优秀的博客等 ReactNativeMaterials 资料
概述目前,React Native的版本是0.28,主要的动画分为两大类
目前React native的release速度还是比较快的,每隔2周左右就release一次。 准备工作本文默认读者已经
一个简单的动画一个最基本的Animated创建过程如下
基于上述的原理,我们来实现第一个动画。 创建一个Demo工程的时候,运行后,模拟器截图应该是酱紫的。 然后,只保留第一行文字,然后我们给这个默认的视图创建fade in动画,效果如下 代码 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>
);
}
}
所以说,简单的动画就是用 在当前版本0.27种,可动画的视图包括
Animated详解方法
属性
AnimatedValue类型一个AnimatedValue一次可以驱动多个可动画属性,但是一个AnimatedValue一次只能由一个机制驱动。比如,一个Value可以同时动画View的透明度和位置,但是一个Value一次只能采用线性时间函数 方法
AnimatedValueXY和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或者手势来手动的控制动画的过程。这就是我接下来要讲的。 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
])
Scroll驱动目标效果 - 随着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,});
}
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>
);
}
}
LayoutAnimationLayoutAnimation在View由一个位置变化到另一个位置的时候,在下一个Layout周期自动创建动画。通常在setState前掉用 一个简单的Demo代码 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详解配置相关//配置下一次切换的效果,其中config可配置的包括duration(时间),create(配置新的View),update(配置更新的View)
static configureNext(config,onAnimationDidEnd?)
//configureNext的方便方法
static create(duration,type,creationProp) #
属性对应三种时间函数 easeInEaSEOut: CallExpression #
linear: CallExpression #
spring: CallExpression #
Navigator转场动画我们先创建一个默认的Navigator转场Demo 这时候的核心代码如下,MainScreen和DetailScreen就是带一个Button的视图 class Demo extends React.Component{
render(){
return (
<Navigator
style = {styles.container}
initialRoute={{id:"main",}}
renderScene={this.renderNav}
configureScene={(route,routeStack) => Navigator.SceneConfigs.PushFromRight}
/>
);
}
renderNav(route,nav){
switch (route.id) {
case 'main':
return <MainScreen navigator={nav} title="Main"/ >;
case 'detail':
return (<DetailScreen navigator={nav} title="Detail"/ >);
}
}
}
Navigator的默认的转场动画的实现都可以在这里找到NavigatorSceneConfigs.js。 So,我们有两种方式来实现自定义的转场动画
篇幅限制,本文只修改默认的转场 比如,我想把默认的PushFromRight动画中,第一个视图的移动距离改为全屏幕。 var ToTheLeftCustom = {
transformTranslate: {
from: {x: 0,y: 0,z: 0},to: {x: -SCREEN_WIDTH,//修改这一行
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}
这时候的动画如下 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |