Flutter 動(dòng)畫詳解(二)

本文通過代碼層面去分析Flutter動(dòng)畫的實(shí)現(xiàn)過程汽纤,介紹了Flutter中的Animation庫以及Physics庫动猬。

1. 介紹

本文會從代碼層面去介紹Flutter動(dòng)畫,因此不會涉及到Flutter動(dòng)畫的具體使用。

1.1 Animation庫

Flutter的animation庫只依賴兩個(gè)庫绅项,Dart庫以及physics庫。animation是采用Dart編寫的比肄,所以依賴Dart庫是很正常的快耿。physics庫是什么呢?

Simple one-dimensional physics simulations, such as springs, friction, and gravity, for use in user interface animations.

physics庫是一個(gè)簡單的物理模擬的庫芳绩,包含彈簧掀亥、阻尼、重力等物理效果妥色。前篇文章介紹過Flutter動(dòng)畫搪花,F(xiàn)lutter動(dòng)畫兩個(gè)分類中的一個(gè)就是基于物理的動(dòng)畫(Physics-based animation)。所以可以猜測出animation庫中有一部分代碼嘹害,是實(shí)現(xiàn)了另一種動(dòng)畫--補(bǔ)間動(dòng)畫(Tween Animation)撮竿。

通過這種庫的劃分,也可以大致猜測出笔呀,基于物理動(dòng)畫的庫是后續(xù)添加的幢踏。這說明了什么呢?

  • 補(bǔ)間動(dòng)畫是現(xiàn)代移動(dòng)端相對基礎(chǔ)的動(dòng)畫類型许师,這個(gè)是必須的房蝉;
  • 基于物理動(dòng)畫是在體驗(yàn)上的改善添加上去的,大致可以猜測出為iOS端的體驗(yàn)優(yōu)化微渠;

1.2 Physics庫

Flutter基于物理的動(dòng)畫惨驶,實(shí)際上是相當(dāng)簡單的。目前實(shí)現(xiàn)了彈簧敛助、阻尼粗卜、重力三種物理效果,整個(gè)庫的代碼量也不多纳击。詳細(xì)的代碼在下面的部分介紹续扔,在此處攻臀,我們先來說下基于物理的動(dòng)畫庫的原理。

基于物理的動(dòng)畫纱昧,給我們的感覺會更真實(shí)刨啸,這是因?yàn)槠涓先藗內(nèi)粘I畹母泄佟@缱鲆粋€(gè)球體下落的動(dòng)畫识脆,如果是勻速的下落设联,給人的感覺會不夠真實(shí),實(shí)際的生活經(jīng)驗(yàn)告訴我們灼捂,球體自由下落應(yīng)該是會有先慢后快的一個(gè)過程离例。如果讓我們自己去實(shí)現(xiàn)這么一個(gè)動(dòng)畫效果,我們會怎么去處理呢悉稠?

高中物理我們學(xué)習(xí)過自由落體相關(guān)的概念宫蛆,其中的位移計(jì)算公式:

s = 1/2 * g * t * t

從公式中我們知道,自由落體的位移跟時(shí)間不是線性關(guān)系的猛。我們可以根據(jù)這個(gè)公式耀盗,來實(shí)時(shí)的計(jì)算出位移來。

如果是摩擦阻尼或者彈簧呢卦尊,也都有相關(guān)的物理公式叛拷,我們所謂的基于物理的動(dòng)畫庫,也就是基于此類公式來實(shí)現(xiàn)的岂却,本質(zhì)上還是補(bǔ)間動(dòng)畫胡诗,只不過過程遵循物理規(guī)律比較復(fù)雜罷了。

2. Animation庫

講解這一部分淌友,也考慮過將Flutter的動(dòng)畫原理先介紹一下。想了想骇陈,前一篇文章介紹過這些普世的動(dòng)畫原理震庭,F(xiàn)lutter只不過是特定平臺的實(shí)現(xiàn),無非是實(shí)現(xiàn)手段的不同你雌,因此器联,F(xiàn)lutter動(dòng)畫原理的解析,放到本文最后的小節(jié)部分婿崭,在代碼的基礎(chǔ)上去解釋拨拓,筆者覺得更加好理解。

2.1 animation.dart

animation.dart定義了動(dòng)畫的四種狀態(tài)氓栈,以及核心的抽象類Animation渣磷。

2.1.1 動(dòng)畫的四種狀態(tài)

這個(gè)文件中定義了Animation的四種狀態(tài):

  • dismissed:動(dòng)畫的初始狀態(tài)
  • forward:從頭到尾播放動(dòng)畫
  • reverse:從尾到頭播放動(dòng)畫
  • completed:動(dòng)畫完成的狀態(tài)

2.1.2 Animation類

Animation類是Flutter動(dòng)畫中核心的抽象類,它包含動(dòng)畫的當(dāng)前值和狀態(tài)兩個(gè)屬性授瘦。定義了動(dòng)畫的一系列回調(diào),

  • 動(dòng)畫過程中值變化的回調(diào):
void addListener(VoidCallback listener);
void removeListener(VoidCallback listener);
  • 狀態(tài)的回調(diào)函數(shù):
void addStatusListener(AnimationStatusListener listener);
void removeStatusListener(AnimationStatusListener listener);

2.2 curve.dart

A curve must map t=0.0 to 0.0 and t=1.0 to 1.0.

看到這段英文醋界,首先會想到什么竟宋?沒錯(cuò),插值器形纺。Curve也是一個(gè)抽象類丘侠,定義了時(shí)間與數(shù)值的一個(gè)接口。

double transform(double t);

例如一個(gè)線性的插值器逐样,實(shí)現(xiàn)代碼如下蜗字。

class _Linear extends Curve {
  const _Linear._();

  @override
  double transform(double t) => t;
}

該文件下面定義了非常多類型的插值器,具體的實(shí)現(xiàn)不一一展開了脂新。Flutter定義了一系列的插值器挪捕,封裝在Curves類中,有下面13種效果戏羽。

  • linear
  • decelerate
  • ease
  • easeIn
  • easeOut
  • easeInOut
  • fastOutSlowIn
  • bounceIn
  • bounceOut
  • bounceInOut
  • elasticIn
  • elasticOut
  • elasticInOut

如果上面的13種還不滿足需求的話担神,還可以使用Cubic類來進(jìn)行自定義的構(gòu)造∈蓟ǎ可以看出這塊兒實(shí)現(xiàn)參考了web中的相關(guān)實(shí)現(xiàn)妄讯。

2.3 tween.dart

該文件定義了一系列的估值器,F(xiàn)lutter通過抽象類Animatable來實(shí)現(xiàn)估值器酷宵。關(guān)于Animatable亥贸,我們可以先看下其定義。

An object that can produce a value of type T given an [Animation<double>]
as input.

可以根據(jù)不同的輸入浇垦,產(chǎn)出不同的數(shù)值炕置。通過重載下面的函數(shù)來產(chǎn)生不同的估值器。

T transform(double t);

它的最主要的子類是Tween男韧,一個(gè)線性的估值器朴摊,實(shí)現(xiàn)如下,非常的簡單此虑,就是一個(gè)線性函數(shù)甚纲。

T lerp(double t) {
  assert(begin != null);
  assert(end != null);
  return begin + (end - begin) * t;
}
  
@override
T transform(double t) {
  if (t == 0.0)
    return begin;
  if (t == 1.0)
    return end;
  return lerp(t);
}

在Tween的基礎(chǔ)上實(shí)現(xiàn)了不同類型的估值器。

  • ReverseTween
  • ColorTween
  • SizeTween
  • RectTween
  • IntTween
  • StepTween
  • ConstantTween

還可以通過自定義的插值器去實(shí)現(xiàn)估值器朦前,例如通過curve實(shí)現(xiàn)的估值器CurveTween介杆。

2.4 animation_controller.dart

動(dòng)畫的控制,就在這個(gè)文件下面實(shí)現(xiàn)韭寸,其中最重要的部分是AnimationController春哨,它派生自Animation類。

AnimationController的功能有如下幾種:

  • 播放一個(gè)動(dòng)畫(forwaed或者reverse)恩伺,或者停止一個(gè)動(dòng)畫赴背;
  • 設(shè)置動(dòng)畫的值;
  • 設(shè)置動(dòng)畫的邊界值;
  • 創(chuàng)建基于物理的動(dòng)畫效果癞尚。

默認(rèn)情況下耸三,AnimationController是線性的產(chǎn)生0.0到1.0之間的值,每刷新一幀就產(chǎn)出一個(gè)數(shù)值浇揩。AnimationController在不使用的時(shí)候需要dispose仪壮,否則會造成資源的泄漏。

2.4.1 TickerProvider

提到AnimationController必須要先說一下TickerProvider胳徽。

An interface implemented by classes that can vend Ticker objects.

TickerProvider定義了可以發(fā)送Ticker對象的接口积锅,

Ticker createTicker(TickerCallback onTick);

Ticker能干什么呢?

Tickers can be used by any object that wants to be notified whenever a frame triggers.

它的主要作用是獲取每一幀刷新的通知养盗,作用就顯而易見了缚陷,相當(dāng)于給動(dòng)畫添加了一個(gè)動(dòng)起來的引擎。

2.4.2 AnimationController

現(xiàn)在再次回到AnimationController往核。上面為什么要先說一下TickerProvider呢箫爷,這是因?yàn)锳nimationController的構(gòu)造函數(shù)中需要一個(gè)TickerProvider參數(shù)。

結(jié)合上面介紹的插值器聂儒、估值器以及Ticker回調(diào)虎锚,AnimationController大致的工作流程,我相信很多人都可以理出來了衩婚。

隨著時(shí)間的流逝窜护,插值器根據(jù)時(shí)間產(chǎn)生的值作為輸入,提供給估值器非春,產(chǎn)生動(dòng)畫的實(shí)際效果值柱徙,結(jié)合Ticker的回調(diào),渲染出當(dāng)前動(dòng)畫值的圖像奇昙。這也是補(bǔ)間動(dòng)畫的工作原理护侮。

補(bǔ)間動(dòng)畫

AnimationController具體的源碼不做分析了,可以看到Flutter的動(dòng)畫實(shí)現(xiàn)的其實(shí)是相當(dāng)?shù)脑即⒛停珹nimationController需要一個(gè)觸發(fā)刷新的回調(diào)羊初,輸出也是值的改變,并不像成熟平臺里面的配合View去做動(dòng)畫弧岳。

3. Physics庫

Physics庫基本上就是插值器的實(shí)現(xiàn)部分,這部分比較簡單

Physics動(dòng)畫庫

Simulation定義了基于物理動(dòng)畫的相關(guān)接口业踏,具體有位置禽炬、速度、是否完成以及公差(Tolerance)

double x(double time);
double dx(double time);

GravitySimulation的實(shí)現(xiàn)如下勤家,其中_a加速度腹尖,_x是初始距離,_v是初始速度:

@override
double x(double time) => _x + _v * time + 0.5 * _a * time * time;

@override
double dx(double time) => _v + time * _a;

相信學(xué)過高中物理的讀者伐脖,對這公式不會陌生热幔。其他幾種具體實(shí)現(xiàn)不在此處一一展開了哈乐设。如果擴(kuò)展這個(gè)物理動(dòng)畫庫的話,也很好去擴(kuò)展绎巨,掌握一些物理公式近尚,就可以去仿照實(shí)現(xiàn)了。

4. Ticker

關(guān)于動(dòng)畫的驅(qū)動(dòng)场勤,在此簡單的說一下戈锻,Ticker是被SchedulerBinding所驅(qū)動(dòng)。SchedulerBinding則是監(jiān)聽著Window.onBeginFrame回調(diào)和媳。

Window.onBeginFrame的作用是什么呢格遭,是告訴應(yīng)用該提供一個(gè)scene了,它是被硬件的VSync信號所驅(qū)動(dòng)的留瞳。

具體可以查看sky_engine下面的window.dart的實(shí)現(xiàn)拒迅,不做展開了。

5. 小節(jié)

本篇文章簡單的從代碼的層面解析了一下Flutter的動(dòng)畫她倘,更深入的Ticker這塊兒璧微,感興趣的讀者可以自行去了解,這塊兒涉及到sky_engine下面的代碼帝牡。

本篇文章往毡,筆者依然試圖繞過代碼去講解一些普適性的東西,但是Flutter這塊兒代碼實(shí)現(xiàn)的確實(shí)挺簡單的靶溜,造成的問題是調(diào)用起來費(fèi)勁开瞭。

基于物理的動(dòng)畫,我們要知道深層次的是物理公式罩息,有這個(gè)基礎(chǔ)嗤详,我們才可以制作出符合感官的動(dòng)畫效果。其本質(zhì)也是補(bǔ)間動(dòng)畫瓷炮,過程可以被計(jì)算出來葱色。

可以說的寬泛一些,一般的動(dòng)畫娘香,大部分都是補(bǔ)間動(dòng)畫苍狰,如果我們自行去設(shè)計(jì)一套動(dòng)畫系統(tǒng),插值器烘绽、估值器淋昭、驅(qū)動(dòng)部分以及動(dòng)畫的管理部分,這四個(gè)模塊之間相互協(xié)調(diào)輸出一幀一幀的動(dòng)畫過場安接。絕大部分平臺的動(dòng)畫設(shè)計(jì)翔忽,也都逃不過這些因素,只不過實(shí)現(xiàn)的方式各不相同。

如果文中有錯(cuò)誤的地方歇式,煩請指正驶悟,筆者水平有限,再次感謝材失。

6. 后話

筆者建了一個(gè)Flutter學(xué)習(xí)相關(guān)的項(xiàng)目痕鳍,Github地址,里面包含了筆者寫的關(guān)于Flutter學(xué)習(xí)相關(guān)的一些文章豺憔,會定期更新额获,也會上傳一些學(xué)習(xí)Demo,歡迎大家關(guān)注恭应。

7. 參考

  1. Animations in Flutter
  2. Tutorial: Animations in Flutter
  3. TickerProvider class
  4. Ticker class
  5. SchedulerBinding class
  6. onBeginFrame property
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末抄邀,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子昼榛,更是在濱河造成了極大的恐慌境肾,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胆屿,死亡現(xiàn)場離奇詭異奥喻,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)非迹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門环鲤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人憎兽,你說我怎么就攤上這事冷离。” “怎么了纯命?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵西剥,是天一觀的道長。 經(jīng)常有香客問我亿汞,道長瞭空,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任疗我,我火速辦了婚禮咆畏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘吴裤。我一直安慰自己旧找,他們只是感情好王带,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著疙筹,像睡著了一般势就。 火紅的嫁衣襯著肌膚如雪泉粉。 梳的紋絲不亂的頭發(fā)上护盈,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天灵莲,我揣著相機(jī)與錄音路克,去河邊找鬼潮秘。 笑死琼开,一個(gè)胖子當(dāng)著我的面吹牛枕荞,可吹牛的內(nèi)容都是我干的柜候。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼躏精,長吁一口氣:“原來是場噩夢啊……” “哼渣刷!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起矗烛,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤辅柴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后瞭吃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體碌嘀,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年歪架,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了股冗。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡和蚪,死狀恐怖止状,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情惠呼,我是刑警寧澤导俘,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站剔蹋,受9級特大地震影響旅薄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜泣崩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一少梁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧矫付,春花似錦凯沪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽挺举。三九已至,卻和暖如春烘跺,著一層夾襖步出監(jiān)牢的瞬間湘纵,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工滤淳, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留梧喷,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓脖咐,卻偏偏與公主長得像铺敌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子屁擅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容