Flutter 實現自定義頁面切換轉場動畫

fluro 轉場動畫源碼

在使用自定義轉場動畫前,先扒一扒 fluro 的源碼,通過源碼可以發現這麼一個標準的轉場方法:

RouteTransitionsBuilder _standardTransitionsBuilder(
      TransitionType? transitionType) {
    return (BuildContext context, Animation<double> animation,
        Animation<double> secondaryAnimation, Widget child) {
      if (transitionType == TransitionType.fadeIn) {
        return FadeTransition(opacity: animation, child: child);
      } else {
        const Offset topLeft = const Offset(0.0, 0.0);
        const Offset topRight = const Offset(1.0, 0.0);
        const Offset bottomLeft = const Offset(0.0, 1.0);

        Offset startOffset = bottomLeft;
        Offset endOffset = topLeft;
        if (transitionType == TransitionType.inFromLeft) {
          startOffset = const Offset(-1.0, 0.0);
          endOffset = topLeft;
        } else if (transitionType == TransitionType.inFromRight) {
          startOffset = topRight;
          endOffset = topLeft;
        } else if (transitionType == TransitionType.inFromBottom) {
          startOffset = bottomLeft;
          endOffset = topLeft;
        } else if (transitionType == TransitionType.inFromTop) {
          startOffset = Offset(0.0, -1.0);
          endOffset = topLeft;
        }

        return SlideTransition(
          position: Tween<Offset>(
            begin: startOffset,
            end: endOffset,
          ).animate(animation),
          child: child,
        );
      }
    };
  }

從源碼可以看出,根據不同枚舉返回了不同的動畫(即 transitionBuilder),其中TransitionType.fadeIn使用的是Flutter 自帶的 FadeTransition,通過改變透明度來完成動畫。而其他的左滑入、右滑入、下滑入和上滑入都是從初始偏移位置滑動到結束位置,使用的是 SlideTransition。Flutter 除了上述的 FadeTransitionSlideTransition 之外,還有如下的常用轉場形式:

  • RotationTransition:旋轉轉場
  • ScaleTransition:縮放轉場

既然是這樣,我們可以依葫蘆畫瓢,先用系統其他的轉場效果做一個自定義的轉場看看。

旋轉轉場動畫

先來看看旋轉的轉場RotationTransition,RotationTransition 的構造方法定義如下:

const RotationTransition({
    Key? key,
    required Animation<double> turns,
    this.alignment = Alignment.center,
    this.child,
  })  : assert(turns != null),
        super(key: key, listenable: turns);

其中 turns 是動畫控制,表示旋轉的弧度數,等於動畫控制值乘以2π。alignment 表示旋轉圍繞的中心位置,默認是居中的。旋轉的弧度不要太大,否則動畫過快,導致不太好看,經過驗證,推薦的起始值0.2至0.3之間,結束值爲0表示回到正常位置。起始值如果爲負,則是順時針;如果爲正則是逆時針,示例代碼如下:

//逆時針圍繞中心旋轉
RouterManager.router.navigateTo(
  context,
  RouterManager.transitionPath,
  transition: TransitionType.custom,
  transitionBuilder:
      (context, animation, secondaryAnimation, child) {
    return RotationTransition(
      turns: Tween<double>(
        begin: 0.25,
        end: 0.0,
      ).animate(animation),
      child: child,
    );
  },
);

//...
//順時針圍繞左下角旋轉
RouterManager.router.navigateTo(
  context,
  RouterManager.transitionPath,
  transition: TransitionType.custom,
  transitionBuilder:
      (context, animation, secondaryAnimation, child) {
    return RotationTransition(
      alignment: Alignment.bottomLeft,
      turns: Tween<double>(
        begin: -0.25,
        end: 0.0,
      ).animate(animation),
      child: child,
    );
  },
);

其中 Tween 是系統自帶的線性插值方法。

縮放轉場動畫

縮放轉場這類在圖片預覽會比較常見,一般是從較小的比例縮放到1:1比例。使用方式和旋轉轉場類似,示例代碼如下:

RouterManager.router.navigateTo(
  context,
  RouterManager.transitionPath,
  transition: TransitionType.custom,
  transitionBuilder:
      (context, animation, secondaryAnimation, child) {
    return ScaleTransition(
      scale: Tween<double>(
        begin: 0.5,
        end: 1.0,
      ).animate(animation),
      child: child,
    );
  },
);

自定義轉場動畫

通過閱讀源碼,其實可以發現RotationTransitionScaleTransition 都是繼承自 AnimatedWidget,因此我們可以自己寫一個自定義的 Transition 繼承自 AnimatedWidget,在 build方法中返回一個 Transform 對象即可。通過這種方式可以做自定義的轉場動畫效果。我們以變形爲例,可以利用 Matrix4skew 方法,在 x 和 y 軸進行變形,就可以得到轉場類似卡片變形的效果。也可以只在 X 軸或 Y 軸變形(skewXskewY 方法)。這裏以 x,y 軸同時變形定義了一個轉場動畫:

class SkewTransition extends AnimatedWidget {
  const SkewTransition({
    Key key,
    Animation<double> turns,
    this.alignment = Alignment.center,
    this.child,
  })  : assert(turns != null),
        super(key: key, listenable: turns);

  Animation<double> get turns => listenable as Animation<double>;

  final Alignment alignment;

  final Widget child;

  @override
  Widget build(BuildContext context) {
    final double turnsValue = turns.value;
    final Matrix4 transform =
        Matrix4.skew(turnsValue * pi * 2.0, turnsValue * pi * 2.0);
    return Transform(
      transform: transform,
      alignment: alignment,
      child: child,
    );
  }
}

使用方式和 RotationTransition 類似:

RouterManager.router.navigateTo(
  context,
  RouterManager.transitionPath,
  transition: TransitionType.custom,
  transitionBuilder:
      (context, animation, secondaryAnimation, child) {
    return SkewTransition(
      turns: Tween<double>(
        begin: -0.05,
        end: 0.0,
      ).animate(animation),
      child: child,
    );
  },
);

實際也可以嘗試使用圍繞 X 軸旋轉,圍繞 Y 軸旋轉,以及變更中心點位置(alignment)來實現不同的動畫轉場效果。如果需要更爲複雜的動畫效果,則可以研究動畫的實現,後續篇章將有對應動畫的介紹。

運行效果

運行效果如下圖所示:

總結

本篇介紹了 fluro 導航到其他頁面的自定義轉場動畫實現,Flutter本身提供了不少預定義的轉場動畫,可以通過 transitionBuilder 參數設計多種多樣的轉場動畫,也可以通過自定義的 AnimatedWidget實現個性化的轉場動畫效果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章