Flutter動畫 4 - Hero動畫


概述


花瓣App的轉(zhuǎn)場動畫,這么多年還是沒變,還是圖片轉(zhuǎn)場動畫.

網(wǎng)上有很多人實現(xiàn)過相關(guān)效果,先前騷棟18年在做iOS項目的時候,也實現(xiàn)的類似的效果,但是全程實現(xiàn)起來還是比較麻煩,需要自己來定義轉(zhuǎn)場動畫.在Flutter中也提供了相關(guān)效果的組件,那就是 <font color="#FF0000">Hero</font> . 接下來我們就來看一下Hero組件實現(xiàn)這種轉(zhuǎn)場動畫效果,整體來說還是比較簡單的.


Hero動畫


首先看一下Hero的構(gòu)建方法.

其中有兩個參數(shù)是必須要傳入的,一個是final Object tag;,是兩個路由需要實現(xiàn)Hero動畫組件的唯一標(biāo)識.另外一個就是final Widget child;,是需要執(zhí)行動畫的具體視圖組件,可以是個頭像,圖片,文字等等都是可以的.

  const Hero({
    Key key,
    @required this.tag,
    this.createRectTween,
    this.flightShuttleBuilder,
    this.placeholderBuilder,
    this.transitionOnUserGestures = false,
    @required this.child,
  }) : assert(tag != null),
       assert(transitionOnUserGestures != null),
       assert(child != null),
       super(key: key);

接下來我們就以一個普通Container組件為例,看一下具有應(yīng)該怎么實現(xiàn)Hero動畫.

首先,我們先創(chuàng)建一個StatelessWidget - FlutterHeroAnimationFirstPage,當(dāng)做起始路由.在build構(gòu)建方法中,我們創(chuàng)建一個Hero組件和一個用于點擊跳轉(zhuǎn)的Button組件.Hero組件定義Tag值為HeroAnimationTag.代碼如下所示,

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Hero(
              tag: "HeroAnimationTag",
              child: Container(
                width: 100,
                height: 100,
                margin: EdgeInsets.only(bottom: 10),
                color: Colors.redAccent,
              ),
            ),
            FlatButton(
              onPressed: (){_startHeroAnimation(context);},
              child: Text(
                "點擊執(zhí)行Hero動畫",
                style: TextStyle(color: Colors.black38),
              ),
            ),
          ],
        ),
      ),
    );
  }

對于路由調(diào)轉(zhuǎn)方案,雖然使用其他路由形式也是可以的,但是這里我推薦使用 PageRouteBuilder + FadeTransition 兩者配合使用,具體路由跳轉(zhuǎn)方法_startHeroAnimation代碼如下所示.

  void _startHeroAnimation(BuildContext context) {
    Navigator.push(context, PageRouteBuilder(
        pageBuilder: (BuildContext context, Animation animation,
            Animation secondaryAnimation) {
          return new FadeTransition(
            opacity: animation,
            child: FlutterHeroAnimationSecondPage(),
          );
        })
    );
  }

然后第二路由中的Widget就比較簡單了. 在build構(gòu)建方法中, 依然需要創(chuàng)建一個Hero組件,并且Tag值要與先前的保持一致.當(dāng)然了,這里我也創(chuàng)建了一個Button,用于返回上一個路由.具體代碼如下所示.

class FlutterHeroAnimationSecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          children: [
            Hero(
              tag: "HeroAnimationTag",
              child: Container(
                height: 100,
                margin: EdgeInsets.only(bottom: 10, left: 0, right: 0),
                color: Colors.redAccent,
              ),
            ),
            FlatButton(
              onPressed: () {Navigator.pop(context);},
              child: Text(
                "點擊返回",
                style: TextStyle(color: Colors.black38),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Hero動畫實現(xiàn)效果如下所示.


整體示例代碼


整體代碼實現(xiàn)比較簡單,這里就不傳Github了,直接貼出來了.具體如下所示.


class FlutterHeroAnimationFirstPage extends StatelessWidget {

  void _startHeroAnimation(BuildContext context) {
    Navigator.push(context, PageRouteBuilder(
        pageBuilder: (BuildContext context, Animation animation,
            Animation secondaryAnimation) {
          return new FadeTransition(
            opacity: animation,
            child: FlutterHeroAnimationSecondPage(),
          );
        })
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Hero(
              tag: "HeroAnimationTag",
              child: Container(
                width: 100,
                height: 100,
                margin: EdgeInsets.only(bottom: 10),
                color: Colors.redAccent,
              ),
            ),
            FlatButton(
              onPressed: (){_startHeroAnimation(context);},
              child: Text(
                "點擊執(zhí)行Hero動畫",
                style: TextStyle(color: Colors.black38),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class FlutterHeroAnimationSecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          children: [
            Hero(
              tag: "HeroAnimationTag",
              child: Container(
                height: 100,
                margin: EdgeInsets.only(bottom: 10, left: 0, right: 0),
                color: Colors.redAccent,
              ),
            ),
            FlatButton(
              onPressed: () {Navigator.pop(context);},
              child: Text(
                "點擊返回",
                style: TextStyle(color: Colors.black38),
              ),
            ),
          ],
        ),
      ),
    );
  }
}


結(jié)語


Hero動畫實現(xiàn)過程比較簡單,所有的工作都是由Flutter內(nèi)部幫我們完成的,Flutter Framework知道新舊路由頁中共享元素的位置和大小,所以根據(jù)這兩個端點,在動畫執(zhí)行過程中求出過渡時的插值(中間態(tài))即可.

歡迎持續(xù)關(guān)注騷棟,有任何問題歡迎聯(lián)系騷棟.


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容