react native動畫--金幣落下&數字滾動效果實現

最近寫rn項目遇到動畫需求,分享下代碼供有需求的前端er參考~
先上最終效果圖,以下css

開始分解動畫!

第一步:獲取動畫起點位置,首先點擊時獲取動畫開始位置,利用mesure方法獲取元素距離屏幕左上角位置;前端

task-list.js文件react

方法參數:task-此條任務信息,object;mount-可領取金幣數額 numberspring

doneCallBack=(task, mount)=>{
    let refname = `goldimg${task.task_id}`;
    let X,Y;
    this.refs[refname].measure((frameX, frameY, frameWidth, frameHeight, pageX, pageY)=>{
      X=pageX;
      Y=pageY;
      this.props._showAnimate(true, pageX, pageY, mount); 
    });
  };
複製代碼

第二步:task-list.js父組件main.js中執行三部分動畫;數組

1 清除上一次動畫定時器,並不可見動畫;
2 存儲位置信息,將金幣 如60,拆分紅[6, 0],以建立數字滾動動畫;
3 執行金幣動畫,金幣背景動畫,數字動畫
bash

方法參數:status- boolean 是否須要執行動畫;X-number 距離屏幕左側距離;Y-number 距離屏幕頂部距離;mount-number 可領取金幣數額;函數

_showAnimate=(status, X, Y ,mount)=>{
    this.timer && clearTimeout(this.timer); 
    this.state.isShowAnim &&         this.setState({isShowAnim:false}); //上兩行防止屢次點擊出現混亂
    this.setState({
      clickedPositionX:X,
      clickedPositionY:Y,
      mount:mount.toString().split(''),
      isShowAnim:true,
    }, ()=> {
      if(status){ 
      this.goldAnimate._startGoldAnimate();// 執行金幣動畫
        this.goldBgAnimate._startGoldAnimate(); // 執行金幣背景動畫
        this.goldnumRunAnimate._startAnimate(); // 執行數字滾動動畫
        this._getTaskList(); // 從新獲取任務數據
      }
    });
    // 4s後去掉領取獎勵動畫彈窗
    this.timer = setTimeout(()=>{
      this.setState({
        isShowAnim:false
      })
    }, 4000)
  };
複製代碼

上金幣動畫代碼:gold-animate.js動畫

使用rn內置Animated模塊,
三種動畫效果
1 spring - 彈簧物理模型,
2 timing - easing函數(大多數狀況下使用);
3 decay - 指定的初始速度開始,而後速度變慢至停下;
動畫先向上(x)彈40,而後向右下(x,y)同時移動
translateYVal是new Animated.Value(0)動畫初始值,爲0;
useNativeDriver:true 使用原生驅動加載動畫,能夠提升動畫流暢,可是對css動畫部分支持;
interpolate插值函數,具體參考官方文檔;
clickedPositionX 點擊發生時距離屏幕左側;
finalPosY 金幣背景的Y位置
ui

js部分this

_startGoldAnimate=()=>{ 
    let spring = Animated.spring, timing = Animated.timing, parallel = Animated.parallel;
    spring(
        this.state.translateYVal,
        {
          toValue:1,
          duration:500,
          friction:3, 
          tension:50,
          useNativeDriver:true
        }
    ).start();
    parallel([
      timing(
          this.state.translateXVal,
          {
            toValue:1,
            duration:300,
            useNativeDriver:true,
            delay:600
          }
      ),
      timing(
          this.state.translateSecYVal,
          {
            toValue:1,
            duration:300,
            useNativeDriver:true,
            delay:600
          }
      )
    ]).start();
  };
複製代碼

react部分

return (
        <Animated.View
            style={{
              position:'absolute',
              zIndex:10000,
              height:'100%',
              width:'100%',
              left:clickedPositionX - 5,
              top:clickedPositionY - 5,     
              transform:[
                {
                  translateY:translateYVal.interpolate({
                    inputRange: [0, 0.7, 1],
                    outputRange: [0, -40, 0]
                  })
                },
              ]
            }}
        >
          <Animated.Image
              style={{
                height:22,
                width:22,
                transform:[
                  {
   translateX:translateXVal.interpolate({
                      inputRange: [0, 1],
                      outputRange: [0, Utils.windowSize.width - clickedPositionX - 89]
                    })
                  },
                  {      translateY:translateSecYVal.interpolate({
                      inputRange: [0, 1],
                      outputRange: [0, finalPosY - clickedPositionY + 15 ]
                    })
                  },     
                ],
              }}
              source={require('resource/newtask/animategold.png')}
          >
          </Animated.Image>
        </Animated.View>
    )
複製代碼

代碼繼續,按順序金幣背景動畫
gold-bg-animate.js
動畫部分:背景向上Y,移動60

_startGoldAnimate = () => {
    Animated.timing(
        this.state.animValue,
        {
          toValue:1,
          duration:1000,
          useNativeDriver:true
        }
    ).start();
  }
複製代碼

react部分

return (
      <View
          ref={(view) => this.goldNumBg = view}
          style={styles.container}
      >
        <Animated.Image
            style={{
              position:'absolute',         
              bottom:-60,
              right:0,
              height:55,
              width:104,
              transform:[
                {
                  translateY:animValue.interpolate({
                    inputRange: [0, 1],
                    outputRange: [0,-60]
                  })
                }
              ],
            }}
            source={require('resource/newtask/animate_gold_bg.png')}
        >
        </Animated.Image>
      </View>
    )
複製代碼

接下來最後一個動畫,數字滾動動畫
文件goldnum-run-animate.js
動畫部分,第一個透明度從0到1;每一個數字依次開始動畫,注意裏面的delay屬性;

_startAnimate = () => {   
    Animated.timing(
        this.state.opacityAnim,
        {
          toValue:1,
          duration:1000,
        }
    ).start();
    this.state.animAry.forEach((item, index) => {
      Animated.timing(
          item.value,
          {
            toValue:1,
            duration:(Number(item.num)+1) * 200,
            delay:index * 400
          }
      ).start();
    });
  };
複製代碼

animAry爲建立出數組,爲每一個數字添加動畫初始值;

let numAry = this.state.mount;
    let animAry = [];
    for (let i = 0; i < numAry.length; i++) {
      animAry.push({
        num: numAry[i],
        name: `animValue${i}`,
        value: new Animated.Value(0),
      })
    }
複製代碼

react部分:mount就是上文提到過的數字改造的數組,60=》[6, 0]

return (
        <View style={styles.numContainer}>
          <Animated.Text
              style={[
                styles.goldText,
                styles.addMark,
                {
                  opacity:opacityAnim.interpolate({
                    inputRange:[0, 1],
                    outputRange:[0, 1]
                  })
                }
              ]}
          >
            +
          </Animated.Text>
          {
            mount.map((item,index) => {
              return (
                  <Animated.View
                      key={index}
                      style={[styles.textcon,{
                        height:(Number(item)+1) * 30,
                        top:this.createAnimate(item, index)
                      }]}>
                    {
                      this._createSingleNum(item)
                    }
                  </Animated.View>
              )
            })
          }
        </View>
    )
複製代碼

_createSingleNum(item)方法以下:

參數 item-傳入的數字,number;

_createSingleNum = (num) => {
    let ary =[];
    for (let i =0; i <= num; i++) {
      ary.push(i)
    }
    return (
        <View>
          {
            ary.map((item,index)=>{
                return (<Text key={index} style={[styles.goldText]}>{item}</Text>)
            })
          }
        </View>
    )
  };
複製代碼

建立的結果以下,每一個數字列有個動畫,從0-5滾動;

<View>
  <Text>0<Text>   
  <Text>1<Text>
  <Text>2<Text>
  <Text>3<Text>
  <Text>4<Text>
  <Text>5<Text>
<View>

複製代碼

createAnimate()方法,
參數:it-數字,number 例如:5 ind-索引值,number,如 0;
爲數組中每一個數字,建立動畫,最終經過top值改變行程動畫

createAnimate = (it, ind) => {
    let propName = `animValue${ind}`;
    return (
 this.state.animAry[ind].value.interpolate({
          inputRange: [0, 1],
          outputRange: [30,-30 * it]
        })
    )
  };
複製代碼

最終三個動畫實現文中開頭動畫。就醬吧~

相關文章
相關標籤/搜索