1. 程式人生 > >React Native動畫之Animated仿網易雲音樂啟動動畫

React Native動畫之Animated仿網易雲音樂啟動動畫

       動畫對於一款APP的重要性,我想不用多說,想必不是搞開發的也明白,雖說APP的簡潔實用性很重要,但UE也是同等重要的。

      下面分析下網易雲音樂的啟動動畫,一張開啟圖片縮放的同時首頁也進行縮放,不過首頁初始化的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中進行宣告:

constructor(props){
    super(props);
    //初始化兩個變數,一個用於操作scale,一個用於操作opacity.
    this.state={
      bounceAnimValue:new Animated.Value(1),
      opacityAnimValue:new Animated.Value(1),
    };
  }
然後在componentDidMount中進行啟動:
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:

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>
    );
  }

注意:只有宣告為可動畫化的元件才能被關聯動畫。ViewText,還有Image都是可動畫化的。

如果你想讓自定義元件可動畫化,可以用createAnimatedComponent。如:

const AnimatedIcon = Animated.createAnimatedComponent(Icon);
。。。
<AnimatedIcon 
        size={60} 
        style={[styles.twitter,{transform:[{scale:this.state.transformAnim}]}]} 
        name="social-twitter"/>

以上是針對splash啟動頁的操作,同理我們也要對首頁進行相應的動畫,下面主要看下首頁的render函式:

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:

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佈局這樣就實現了這種形式。


至此我們就寫完了,下面我們看下效果吧


原始碼連結

RN開發群:527459711.歡迎大夥加入.