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

上一篇文章Flutter 動(dòng)畫(huà)詳解系列概述了下Flutter中的動(dòng)畫(huà)類型,及如何選擇恰當(dāng)?shù)膭?dòng)畫(huà)創(chuàng)建方式,接下來(lái)我們來(lái)看下最簡(jiǎn)單的動(dòng)畫(huà),隱式動(dòng)畫(huà)。

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

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

implicit_animation.png

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

 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)畫(huà)效果時(shí),矩形的形變會(huì)顯得十分生硬,如果使用AnimatedContainer替換Container,增加一個(gè)動(dòng)畫(huà)的過(guò)渡效果:

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

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

同樣我們也可以通過(guò)插值來(lái)修改AnimatedContainer的其它屬性,包括decoration的漸變色:

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

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

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

另外除了系統(tǒng)自帶的曲線效果外,我們還可以通過(guò)繼承Curve來(lái)實(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)畫(huà)的Widget,這些Widget是普通Widget的動(dòng)畫(huà)版本,我們可以通過(guò) durationcurve來(lái)控制動(dòng)畫(huà)效果。

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

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)畫(huà)Widget不能滿足你的需求,那么進(jìn)一步的話可以試試使用TweenAnimationBuilder來(lái)自定義創(chuàng)建隱式動(dòng)畫(huà)。

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

使用TweenAnimationBuilder,該 Widget 使用的時(shí)候我們需要傳遞 duration 參數(shù)動(dòng)畫(huà)時(shí)間、tween 參數(shù)動(dòng)畫(huà)要設(shè)置的值的范圍(補(bǔ)間)、重要的還有 builder 參數(shù),builder函數(shù)的參數(shù)包含context、補(bǔ)間參數(shù)tween的類型、還有child,讓我們看一個(gè)簡(jiǎn)單的例子,紅色的矩形框旋轉(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è)賮?lái)看個(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),
    );
  },
)

通過(guò)Tween補(bǔ)間參數(shù)設(shè)置了從白色到紅色的過(guò)渡,由顏色和圖片的混合,另外如何補(bǔ)間參數(shù)可變的,所以如果補(bǔ)間參數(shù)是不變的話可以將參數(shù)聲明為靜態(tài)常量來(lá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ù)

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

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 的終值,通過(guò)滑動(dòng) Slider Widget,我們改變_newColor。調(diào)用setState每次的滑動(dòng)都會(huì)使得動(dòng)畫(huà)更新。

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

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

補(bǔ)充說(shuō)明

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

  • curve,動(dòng)畫(huà)曲線,在上一篇文章Flutter 動(dòng)畫(huà)詳解系列有過(guò)介紹。
  • 動(dòng)畫(huà)完成的回調(diào) onEnd :,我們可以在動(dòng)畫(huà)完成時(shí)完成指定的操作,如動(dòng)畫(huà)完成后顯示另一個(gè)Widget。
  • child參數(shù),child參數(shù)的設(shè)置其實(shí)也是一個(gè)潛在的性能優(yōu)化項(xiàng),正確的設(shè)置child,對(duì)動(dòng)畫(huà)性能的提升也是一大幫助。例子中雖然顏色發(fā)生了變化,但圖片本身保持不變,但是當(dāng)前代碼是每次build都會(huì)重新創(chuàng)建Image Widget。針對(duì)此類的優(yōu)化方式,我們可以提前創(chuàng)建圖片,將圖片作為參數(shù)傳入,這樣 Flutter 就知道每幀渲染時(shí)變化的是顏色,而不是圖片本身。 當(dāng)然因?yàn)槔颖旧砗?jiǎn)單,所以此優(yōu)化不會(huì)有明顯的效果,但當(dāng)實(shí)現(xiàn)復(fù)雜動(dòng)畫(huà)效果時(shí),慎重考慮child的實(shí)現(xiàn),將會(huì)對(duì)你的動(dòng)畫(huà)性能帶來(lái)一定的幫助。

總結(jié)

OK,以上就是對(duì) Flutter 中的隱式動(dòng)畫(huà)的介紹了,包括系統(tǒng)自帶的AnimatedFooTweenAnimationBuilder都有了一定的涉及。包括如何不通過(guò)使用StatefulWidget來(lái)實(shí)現(xiàn)動(dòng)畫(huà)效果、如何改變tween的終值產(chǎn)生順滑的動(dòng)畫(huà)效果、如何提升TweenAnimationBuilder的動(dòng)畫(huà)性能時(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)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,983評(píng)論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,772評(píng)論 3 422
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,947評(píng)論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,201評(píng)論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,960評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,350評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,406評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,549評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,104評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,914評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,089評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,647評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,340評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,753評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,007評(píng)論 1 289
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,834評(píng)論 3 395
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,106評(píng)論 2 375