【Flutter学习】之动画实现原理浅析(二)
1. 介紹
本文會(huì)從代碼層面去介紹Flutter動(dòng)畫,因此不會(huì)涉及到Flutter動(dòng)畫的具體使用。
1.1 Animation庫(kù)
Flutter的animation庫(kù)只依賴兩個(gè)庫(kù),Dart庫(kù)以及physics庫(kù)。animation是采用Dart編寫的,所以依賴Dart庫(kù)是很正常的。
physics庫(kù)是什么呢?
Simple one-dimensional physics simulations, such as springs, friction, and gravity, for use in user interface animations.
physics庫(kù)是一個(gè)簡(jiǎn)單的物理模擬的庫(kù),包含彈簧、阻尼、重力等物理效果。前篇文章介紹過(guò)Flutter動(dòng)畫,F(xiàn)lutter動(dòng)畫兩個(gè)分類中的一個(gè)就是基于物理的動(dòng)畫(Physics-based animation)。所以可以猜測(cè)出animation庫(kù)中有一部分代碼,是實(shí)現(xiàn)了另一種動(dòng)畫--補(bǔ)間動(dòng)畫(Tween Animation)。
通過(guò)這種庫(kù)的劃分,也可以大致猜測(cè)出,基于物理動(dòng)畫的庫(kù)是后續(xù)添加的。這說(shuō)明了什么呢?
補(bǔ)間動(dòng)畫是現(xiàn)代移動(dòng)端相對(duì)基礎(chǔ)的動(dòng)畫類型,這個(gè)是必須的;
基于物理動(dòng)畫是在體驗(yàn)上的改善添加上去的,大致可以猜測(cè)出為iOS端的體驗(yàn)優(yōu)化;
1.2 Physics庫(kù)
Flutter基于物理的動(dòng)畫,實(shí)際上是相當(dāng)簡(jiǎn)單的。目前實(shí)現(xiàn)了彈簧、阻尼、重力三種物理效果,整個(gè)庫(kù)的代碼量也不多。詳細(xì)的代碼在下面的部分介紹,在此處,我們先來(lái)說(shuō)下基于物理的動(dòng)畫庫(kù)的原理。
基于物理的動(dòng)畫,給我們的感覺(jué)會(huì)更真實(shí),這是因?yàn)槠涓先藗內(nèi)粘I畹母泄佟@缱鲆粋€(gè)球體下落的動(dòng)畫,如果是勻速的下落,給人的感覺(jué)會(huì)不夠真實(shí),實(shí)際的生活經(jīng)驗(yàn)告訴我們,球體自由下落應(yīng)該是會(huì)有先慢后快的一個(gè)過(guò)程。如果讓我們自己去實(shí)現(xiàn)這么一個(gè)動(dòng)畫效果,我們會(huì)怎么去處理呢?
高中物理我們學(xué)習(xí)過(guò)自由落體相關(guān)的概念,其中的位移計(jì)算公式:
s = 1/2 * g * t * t
從公式中我們知道,自由落體的位移跟時(shí)間不是線性關(guān)系。我們可以根據(jù)這個(gè)公式,來(lái)實(shí)時(shí)的計(jì)算出位移來(lái)。
如果是摩擦阻尼或者彈簧呢,也都有相關(guān)的物理公式,我們所謂的基于物理的動(dòng)畫庫(kù),也就是基于此類公式來(lái)實(shí)現(xiàn)的,本質(zhì)上還是補(bǔ)間動(dòng)畫,只不過(guò)過(guò)程遵循物理規(guī)律比較復(fù)雜罷了。
2. Animation庫(kù)
講解這一部分,也考慮過(guò)將Flutter的動(dòng)畫原理先介紹一下。想了想,前一篇文章介紹過(guò)這些普世的動(dòng)畫原理,F(xiàn)lutter只不過(guò)是特定平臺(tái)的實(shí)現(xiàn),無(wú)非是實(shí)現(xiàn)手段的不同,因此,F(xiàn)lutter動(dòng)畫原理的解析,放到本文最后的小節(jié)部分,在代碼的基礎(chǔ)上去解釋,筆者覺(jué)得更加好理解。
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)畫過(guò)程中值變化的回調(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.
看到這段英文,首先會(huì)想到什么?沒(méi)錯(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類來(lái)進(jìn)行自定義的構(gòu)造。可以看出這塊兒實(shí)現(xiàn)參考了web中的相關(guān)實(shí)現(xiàn)。
2.3 tween.dart
該文件定義了一系列的估值器,F(xiàn)lutter通過(guò)抽象類Animatable來(lái)實(shí)現(xiàn)估值器。關(guān)于Animatable,我們可以先看下其定義。
An object that can produce a value of type
Tgiven an [Animation] as input.
可以根據(jù)不同的輸入,產(chǎn)出不同的數(shù)值。通過(guò)重載下面的函數(shù)來(lái)產(chǎn)生不同的估值器。
T transform(double t);
它的最主要的子類是Tween,一個(gè)線性的估值器,實(shí)現(xiàn)如下,非常的簡(jiǎ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
還可以通過(guò)自定義的插值器去實(shí)現(xiàn)估值器,例如通過(guò)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,否則會(huì)造成資源的泄漏。
2.4.1 TickerProvider
提到AnimationController必須要先說(shuō)一下TickerProvider。
An interface implemented by classes that can vend Ticker objects.
TickerProvider定義了可以發(fā)送Ticker對(duì)象的接口,
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)起來(lái)的引擎。
2.4.2 AnimationController
現(xiàn)在再次回到AnimationController。上面為什么要先說(shuō)一下TickerProvider呢,這是因?yàn)?strong>AnimationController的構(gòu)造函數(shù)中需要一個(gè)TickerProvider參數(shù)。
結(jié)合上面介紹的插值器、估值器以及Ticker回調(diào),AnimationController大致的工作流程,我相信很多人都可以理出來(lái)了。
隨著時(shí)間的流逝,插值器根據(jù)時(shí)間產(chǎn)生的值作為輸入,提供給估值器,產(chǎn)生動(dòng)畫的實(shí)際效果值,結(jié)合Ticker的回調(diào),渲染出當(dāng)前動(dòng)畫值的圖像。這也是補(bǔ)間動(dòng)畫的工作原理。
AnimationController具體的源碼不做分析了,可以看到Flutter的動(dòng)畫實(shí)現(xiàn)的其實(shí)是相當(dāng)?shù)脑迹?strong>AnimationController需要一個(gè)觸發(fā)刷新的回調(diào),輸出也是值的改變,并不像成熟平臺(tái)里面的配合View去做動(dòng)畫。
3. Physics庫(kù)
Physics庫(kù)基本上就是插值器的實(shí)現(xiàn)部分,這部分比較簡(jiǎn)單
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é)過(guò)高中物理的讀者,對(duì)這公式不會(huì)陌生。其他幾種具體實(shí)現(xiàn)不在此處一一展開了哈。如果擴(kuò)展這個(gè)物理動(dòng)畫庫(kù)的話,也很好去擴(kuò)展,掌握一些物理公式,就可以去仿照實(shí)現(xiàn)了。
4. Ticker
關(guān)于動(dòng)畫的驅(qū)動(dòng),在此簡(jiǎn)單的說(shuō)一下,Ticker是被SchedulerBinding所驅(qū)動(dòng)。SchedulerBinding則是監(jiān)聽著Window.onBeginFrame回調(diào)。
Window.onBeginFrame的作用是什么呢,是不好。
具體可以查看sky_engine下面的window.dart的實(shí)現(xiàn),不做展開了。
5. 小節(jié)
基于物理的動(dòng)畫,我們要知道深層次的是物理公式,有這個(gè)基礎(chǔ),我們才可以制作出符合感官的動(dòng)畫效果。其本質(zhì)也是補(bǔ)間動(dòng)畫,過(guò)程可以被計(jì)算出來(lái)。
可以說(shuō)的寬泛一些,一般的動(dòng)畫,大部分都是補(bǔ)間動(dòng)畫,如果我們自行去設(shè)計(jì)一套動(dòng)畫系統(tǒng),插值器、估值器、驅(qū)動(dòng)部分以及動(dòng)畫的管理部分,這四個(gè)模塊之間相互協(xié)調(diào)輸出一幀一幀的動(dòng)畫過(guò)場(chǎng)。絕大部分平臺(tái)的動(dòng)畫設(shè)計(jì),也都逃不過(guò)這些因素,只不過(guò)實(shí)現(xiàn)的方式各不相同。
鏈接:
https://juejin.im/post/5bd700edf265da0aff17860c
總結(jié)
以上是生活随笔為你收集整理的【Flutter学习】之动画实现原理浅析(二)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Angular ng-container
- 下一篇: [windows] Windows 激活