另外,我在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