Flutter 路由動畫Offset小記

yk3372發表於2020-03-03

剛剛看了一下flutter的路由動畫文件,地址是:page-route-animation

PageRouteBuilder(
    pageBuilder: (context, animation, secondaryAnimation) => Page2(),
    transitionsBuilder: (context, animation, secondaryAnimation, child) {
      var begin = Offset(0.0, -1.0);
      var end = Offset.zero;
      var curve = Curves.ease;

      var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));

      return SlideTransition(
        position: animation.drive(tween),
        child: child,
      );
    },
  )
複製程式碼

針對動畫,學習了兩點:

  1. Flutter offset的值是怎麼回事,1 0 -1分別代表什麼,我整理了一下,如圖:

Flutter 路由動畫Offset小記
我來解讀一下,

1. 頁面顯示在螢幕時,Offset的dx dy均為0;
2. 如果需要動畫頁面從螢幕底部彈出,則應該是dy=1 到 dy=0;
3. 如果需要動畫頁面從右側推入到螢幕,則應該是dx=1 到 dx=0;
4. 如果需要動畫頁面從螢幕頂部彈出,則應該是dy=0 到 dy=-1
5. 其他類似
複製程式碼

建議大家使用文件裡的案例操作實驗一下,加深印象。學習了這個,在寫動畫時就不會出現亂套的情況了。

  1. secondaryAnimation是幹嘛用的呢?

寫過安卓的都清楚有進場和出場動畫,overridexxxx,那flutter只通過上面這段程式碼怎麼實現出場,就給了一個child,很是奇怪。然後我發現secondaryAnimation還沒用,是幹嘛的?翻閱的一些文件: buildTransitions

看到了使用方式:

transitionsBuilder: (
      BuildContext context,
      Animation<double> animation,
      Animation<double> secondaryAnimation,
      Widget child,
  ) {
    return SlideTransition(
      position: AlignmentTween(
        begin: const Offset(0.0, 1.0),
        end: Offset.zero,
      ).animate(animation),
      child: SlideTransition(
        position: TweenOffset(
          begin: Offset.zero,
          end: const Offset(0.0, 1.0),
        ).animate(secondaryAnimation),
        child: child,
      ),
    );
  }
複製程式碼

剛看到我是很驚訝的,這是個怎麼操作法?進出都套在了一個child上,到底怎麼執行的呢?一開始我猜想難道是原始碼使用的地方會取出child的child?看了原始碼後,我覺得設計真巧妙!!

首先介紹下這兩個動畫常量(animation.dart中,目的就是保持動畫終止位置和保持動畫起始位置):

kAlwaysCompleteAnimation // The animation is stopped at the end

kAlwaysDismissedAnimation // The animation is stopped at the beginning

看到這裡大家可能會有點明白了,兩個animation是不是用上面兩個常量,來切換使用做到每次只執行一個的呢?

然後我用下面的動畫(一段動畫,新頁面從右側螢幕滑入,舊頁面從螢幕左側滑出),列印了一下對應的回撥:

SlideTransition(
      position: Tween<Offset>(
        begin: const Offset(1.0, 0.0),
        end: Offset.zero,
      ).animate(animation),
      child: SlideTransition(
        position: Tween<Offset>(
          begin: Offset.zero,
          end: const Offset(-1.0, 0.0),
        ).animate(secondAnimation),
        child: child,
      ),
    )
複製程式碼

得到的log,簡化後:

animation sencondaryAnimation
AnimationController#1c77c(▶ 0.335 kAlwaysDismissedAnimation
AnimationController#fc1fe(⏭ 1.000 AAnimationController#1c77c(▶ 0.335
AnimationController#1c77c(▶ 0.535 kAlwaysDismissedAnimation
AnimationController#fc1fe(⏭ 1.000 AAnimationController#1c77c(▶ 0.535
AnimationController#1c77c(⏭ 1.000 kAlwaysDismissedAnimation
AnimationController#fc1fe(⏭ 1.000; AnimationController#1c77c⏭ 1.000

先只看#1c77c這個animation:

發現第一個引數是從0-1做的動畫,第二個引數是保持不變在start狀態即Offset.zero,即通過改變第一個widge的位置,來實現介面從右側滑入,第二個widget一直保持可見狀態。

再來看下fc1fe這個animation,發現這個動畫是第一個widge保持在end狀態即Offset.zero(螢幕上),第二個動畫是從0-1執行,即offset.dx從0 -> -1,也就是頁面從螢幕往左側滑出的動畫。

由以上,我們可以得出結論,animation引數值是用來給新push的頁面做進場動畫的,secondaryAnimation是給前頁面做出廠動畫的,靈活運用兩個動畫的變化值來實現動畫,引數只需要一個child即可,我也從中學習了一種新的思路,建議大家也測試感受下。

相關文章