Flutter 動(dòng)畫詳解系列——隱式動(dòng)畫

上一篇文章Flutter 動(dòng)畫詳解系列概述了下Flutter中的動(dòng)畫類型匹舞,及如何選擇恰當(dāng)?shù)膭?dòng)畫創(chuàng)建方式椿息,接下來我們來看下最簡單的動(dòng)畫涛菠,隱式動(dòng)畫铡恕。

系統(tǒng)的隱式動(dòng)畫Widget

在 Flutter 中的 Widgets 中有一部已經(jīng)實(shí)現(xiàn)隱式動(dòng)畫Widget。如下圖列出部分:

implicit_animation.png

首先我們來看一段未使用動(dòng)畫的代碼:

 bool _bigger = false;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Center(
            child: Container(
              width: _bigger ? 100 : 500,
              height: 100,
              color: Colors.red,
            ),
          ),
          RaisedButton(
            onPressed: () => setState(() {
              _bigger = !_bigger;
            }),
            child: Icon(Icons.star),
          ),
        ],
      ),
    );
  }

未加動(dòng)畫效果時(shí)脏答,矩形的形變會(huì)顯得十分生硬糕殉,如果使用AnimatedContainer替換Container,增加一個(gè)動(dòng)畫的過渡效果:

  Center(
            child: AnimatedContainer(
              width: _bigger ? 100 : 500,
              height: 100,
              color: Colors.red,
              duration: Duration(seconds: 1),
            ),
          ),

整個(gè)過渡過程顯得比較自然順暢殖告,我們通過新舊值之間的值進(jìn)行動(dòng)畫處理的過程稱為插值阿蝶。每當(dāng)舊值和新值發(fā)生變化時(shí),AnimatedContainer便會(huì)處理其屬性插值黄绩。

同樣我們也可以通過插值來修改AnimatedContainer的其它屬性羡洁,包括decoration的漸變色:

AnimatedContainer(
  decoration: BoxDecoration(
    gradient: RadialGradient(
      colors: [Colors.purple, Colors.transparent],
      stops: [ _bigger ? 0.2 : 0.5, 1.0])
  ),
),

上述代碼很簡單的演示如何使用 隱式動(dòng)畫Widget 來實(shí)現(xiàn)動(dòng)畫效果,非常的方便簡單爽丹,但這也意味著可靈活性較差筑煮,在隱式動(dòng)畫Widget中,我們控制動(dòng)畫效果只能控制動(dòng)畫時(shí)長(Duration)和動(dòng)畫的曲線(Curve,具體的曲線效果可以參考 系統(tǒng)自帶的曲線效果)粤蝎。

AnimatedContainer(
  width: _bigger ? 100 : 500,
  child: Image.asset('assets/star.png'),
  duration: Duration(seconds: 1),
  curve: Curves.easeInOutQuint,
),

另外除了系統(tǒng)自帶的曲線效果外真仲,我們還可以通過繼承Curve來實(shí)現(xiàn)自定義的曲線效果,如下實(shí)現(xiàn)了正弦曲線。

class SineCurve extends Curve {
final double count;

SineCurve({this.count = 1});

@override
double transformInternal(double t) {
return sin(count * 2 * pi * t) * 0.5 + 0.5;
}
}

小結(jié)

在Flutter中初澎,系統(tǒng)已經(jīng)提供了隱式動(dòng)畫的Widget秸应,這些Widget是普通Widget的動(dòng)畫版本,我們可以通過 durationcurve來控制動(dòng)畫效果软啼。

還有我們不一定需要通過StatefulWidget中使用setState來生成動(dòng)畫效果桑谍,我們也可以使用StreamBuilderFutureBuilder來觸發(fā)動(dòng)畫。

FutureBuilder(
  future: future,
  builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
    double width;
    switch(snapshot.connectionState) {
      case ConnectionState.none:
      case ConnectionState.waiting:
      case ConnectionState.active:
        width = 0;
        break;
      case ConnectionState.done:
        width = 500;
        break;
    }
    return AnimatedContainer(
      width: width,
      height: 100,
      color: Colors.red,
      duration: Duration(seconds: 1),
    );
  }
),

以上祸挪,如果Flutter框架提供給你的隱式動(dòng)畫Widget不能滿足你的需求锣披,那么進(jìn)一步的話可以試試使用TweenAnimationBuilder來自定義創(chuàng)建隱式動(dòng)畫。

自定義隱式動(dòng)畫TweenAnimationBuilder

使用TweenAnimationBuilder贿条,該 Widget 使用的時(shí)候我們需要傳遞 duration 參數(shù)動(dòng)畫時(shí)間雹仿、tween 參數(shù)動(dòng)畫要設(shè)置的值的范圍(補(bǔ)間)、重要的還有 builder 參數(shù)闪唆,builder函數(shù)的參數(shù)包含context盅粪、補(bǔ)間參數(shù)tween的類型钓葫、還有child,讓我們看一個(gè)簡單的例子悄蕾,紅色的矩形框旋轉(zhuǎn)360度:

TweenAnimationBuilder<double>(
            tween: Tween<double>(begin: 0, end: 2 * pi),
            duration: Duration(seconds: 2),
            builder: (BuildContext context, double angle, Widget child) {
              return Transform.rotate(
                angle: angle,
                child: Container(
                  color: Colors.red,
                  width: 100,
                  height: 100,
                ),
              );
            },
          ),

讓我們?cè)賮砜磦€(gè)例子,使用ColorFilered Wideget做一個(gè)圖片渲染的效果础浮。

TweenAnimationBuilder(
  tween: ColorTween(begin: Colors.white, end: Colors.red),
  duration: Duration(seconds: 2),
  builder: (_, Color color, __) {
    return ColorFiltered(
      child: Image.asset('assets/sun.png'),
      colorFilter: ColorFilter.mode(color, BlendMode.modulate),
    );
  },
)

通過Tween補(bǔ)間參數(shù)設(shè)置了從白色到紅色的過渡帆调,由顏色和圖片的混合,另外如何補(bǔ)間參數(shù)可變的豆同,所以如果補(bǔ)間參數(shù)是不變的話可以將參數(shù)聲明為靜態(tài)常量來使用番刊。

static final colorTween = ColorTween(begin: Colors.white, end: Colors.red);

Center(
          child: TweenAnimationBuilder<Color>(
            tween: colorTween,
            duration: Duration(seconds: 2),
            builder: (_, Color color, __) {
              return ColorFiltered(
                child: Image.asset('assets/sun.png'),
                colorFilter: ColorFilter.mode(color, BlendMode.modulate),
              );
            },
          ),
        ),

動(dòng)態(tài)修改 Tween 參數(shù)

上面的例子中我們并沒有調(diào)用setState,僅僅展示了動(dòng)畫從Tween的初始值到終值的簡單動(dòng)畫效果影锈,除此之外芹务,我們還可以通過動(dòng)態(tài)的修改Tween來實(shí)現(xiàn)動(dòng)畫效果:

class OngoingAnimationByModifyingEndTweenValue extends StatefulWidget {
  @override
  _OngoingAnimationState createState() => _OngoingAnimationState();
}

class _OngoingAnimationState extends State<OngoingAnimationByModifyingEndTweenValue> {
  double _newValue = .4;
  Color _newColor = Colors.white;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        starsBackground,
        Column(
          children: <Widget>[
            Center(
              child: TweenAnimationBuilder(
                tween: ColorTween(begin: Colors.white, end: _newColor),
                duration: Duration(seconds: 2),
                builder: (_, Color color, __) {
                  return ColorFiltered(
                    child: Image.asset('assets/sun.png'),
                    colorFilter: ColorFilter.mode(color, BlendMode.modulate),
                  );
                },
              ),
            ),
            Slider.adaptive(
              value: _newValue,
              onChanged: (double value) {
                setState(() {
                  _newValue = value;
                  _newColor = Color.lerp(Colors.white, Colors.red, value);
                });
              },
            ),
          ],
        ),
      ],
    );
  }
}

首先我們聲明一個(gè)_newColor 作為 Tween 的終值,通過滑動(dòng) Slider Widget,我們改變_newColor鸭廷。調(diào)用setState每次的滑動(dòng)都會(huì)使得動(dòng)畫更新枣抱。

另外,需要明確的一點(diǎn)事辆床,TweenAnimationBuilder 的動(dòng)畫屬性值是從當(dāng)前值向最新的終值轉(zhuǎn)變的佳晶。如上面的例子,當(dāng)我們拖動(dòng) Slider 時(shí)讼载,顏色變化相對(duì)于之前的顏色,而不是每次從最初的白色開始動(dòng)畫漸變轿秧。

TweenAnimationBuilder 總會(huì)將當(dāng)前顏色到終值顏色平滑的動(dòng)畫過渡。所以如果改變的不是終值顏色而是開始顏色咨堤,動(dòng)畫效果是不會(huì)有區(qū)別的菇篡。

補(bǔ)充說明

除了上訴介紹的TweenAnimationBuilder參數(shù)外,我們還需要注意的參數(shù)還有:

  • curve一喘,動(dòng)畫曲線逸贾,在上一篇文章Flutter 動(dòng)畫詳解系列有過介紹。
  • 動(dòng)畫完成的回調(diào) onEnd :,我們可以在動(dòng)畫完成時(shí)完成指定的操作铝侵,如動(dòng)畫完成后顯示另一個(gè)Widget灼伤。
  • child參數(shù),child參數(shù)的設(shè)置其實(shí)也是一個(gè)潛在的性能優(yōu)化項(xiàng)咪鲜,正確的設(shè)置child狐赡,對(duì)動(dòng)畫性能的提升也是一大幫助。例子中雖然顏色發(fā)生了變化疟丙,但圖片本身保持不變颖侄,但是當(dāng)前代碼是每次build都會(huì)重新創(chuàng)建Image Widget。針對(duì)此類的優(yōu)化方式享郊,我們可以提前創(chuàng)建圖片览祖,將圖片作為參數(shù)傳入,這樣 Flutter 就知道每幀渲染時(shí)變化的是顏色炊琉,而不是圖片本身展蒂。 當(dāng)然因?yàn)槔颖旧砗唵危源藘?yōu)化不會(huì)有明顯的效果苔咪,但當(dāng)實(shí)現(xiàn)復(fù)雜動(dòng)畫效果時(shí)锰悼,慎重考慮child的實(shí)現(xiàn),將會(huì)對(duì)你的動(dòng)畫性能帶來一定的幫助团赏。

總結(jié)

OK箕般,以上就是對(duì) Flutter 中的隱式動(dòng)畫的介紹了,包括系統(tǒng)自帶的AnimatedFooTweenAnimationBuilder都有了一定的涉及舔清。包括如何不通過使用StatefulWidget來實(shí)現(xiàn)動(dòng)畫效果丝里、如何改變tween的終值產(chǎn)生順滑的動(dòng)畫效果、如何提升TweenAnimationBuilder的動(dòng)畫性能時(shí)体谒,我們可以設(shè)置tween參數(shù)時(shí)可以考慮設(shè)置為靜態(tài)常量杯聚,設(shè)置child參數(shù)時(shí)可以考慮提前創(chuàng)建好child,作為參數(shù)傳遞营密。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末械媒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子评汰,更是在濱河造成了極大的恐慌纷捞,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,430評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件被去,死亡現(xiàn)場離奇詭異主儡,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)惨缆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門糜值,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丰捷,“玉大人,你說我怎么就攤上這事寂汇〔⊥” “怎么了?”我有些...
    開封第一講書人閱讀 167,834評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵骄瓣,是天一觀的道長停巷。 經(jīng)常有香客問我,道長榕栏,這世上最難降的妖魔是什么畔勤? 我笑而不...
    開封第一講書人閱讀 59,543評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮扒磁,結(jié)果婚禮上庆揪,老公的妹妹穿的比我還像新娘。我一直安慰自己妨托,他們只是感情好缸榛,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,547評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著始鱼,像睡著了一般仔掸。 火紅的嫁衣襯著肌膚如雪脆贵。 梳的紋絲不亂的頭發(fā)上医清,一...
    開封第一講書人閱讀 52,196評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音卖氨,去河邊找鬼会烙。 笑死,一個(gè)胖子當(dāng)著我的面吹牛筒捺,可吹牛的內(nèi)容都是我干的柏腻。 我是一名探鬼主播,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼系吭,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼五嫂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起肯尺,我...
    開封第一講書人閱讀 39,671評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤沃缘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后则吟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體槐臀,經(jīng)...
    沈念sama閱讀 46,221評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,303評(píng)論 3 340
  • 正文 我和宋清朗相戀三年氓仲,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了水慨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片得糜。...
    茶點(diǎn)故事閱讀 40,444評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖晰洒,靈堂內(nèi)的尸體忽然破棺而出朝抖,到底是詐尸還是另有隱情,我是刑警寧澤谍珊,帶...
    沈念sama閱讀 36,134評(píng)論 5 350
  • 正文 年R本政府宣布槽棍,位于F島的核電站,受9級(jí)特大地震影響抬驴,放射性物質(zhì)發(fā)生泄漏炼七。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,810評(píng)論 3 333
  • 文/蒙蒙 一布持、第九天 我趴在偏房一處隱蔽的房頂上張望豌拙。 院中可真熱鬧,春花似錦题暖、人聲如沸按傅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽唯绍。三九已至,卻和暖如春枝誊,著一層夾襖步出監(jiān)牢的瞬間况芒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評(píng)論 1 272
  • 我被黑心中介騙來泰國打工叶撒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留绝骚,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,837評(píng)論 3 376
  • 正文 我出身青樓祠够,卻偏偏與公主長得像压汪,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子古瓤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,455評(píng)論 2 359