<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
最近在寫動畫相關的篇章,經常會用到 Curve
這個動畫曲線類,那這個類到底怎麼實現的?如果想自己來一個自定義的動畫曲線該怎麼弄?本篇我們就來一探究竟。
曲線
檢視原始碼, Curve
類定義如下:
abstract class Curve extends ParametricCurve<double> { const Curve(); @override double transform(double t) { if (t == 0.0 || t == 1.0) { return t; } return super.transform(t); } Curve get flipped => FlippedCurve(this); }
看上去好像沒定義什麼, 實際這裡只是做了兩個處理,一個是明確的資料型別為 double
,另一個是對 transform
做了過載,也只是對引數 t 做了特殊處理,保證引數 t 的範圍在0-1之間,且起點值0.0和終點值1.0不被轉換函數轉換。主要定義在上一層的ParametricCurve
。檔案是建議子類過載transformInternal
方法,那我們就繼續往上看ParametricCurve
這個類的實現,程式碼如下:
abstract class ParametricCurve<T> { const ParametricCurve(); T transform(double t) { assert(t != null); assert(t >= 0.0 && t <= 1.0, 'parametric value $t is outside of [0, 1] range.'); return transformInternal(t); } @protected T transformInternal(double t) { throw UnimplementedError(); } @override String toString() => objectRuntimeType(this, 'ParametricCurve'); }
可以看到,實際上 transform
方法除了做引數合法性驗證以外,其實就是呼叫了transformInternal
方法,因此子類必須要實現該方法,否則會丟擲UnimplementedError
異常。
上面的原始碼可以看到,關鍵在於引數 t
。這個引數 t
代表什麼呢?註釋裡說的是:
Returns the value of the curve at point
t
. — 返回 t 點的曲線對應的值。
因此 t
可以認為是曲線的橫座標,而為了保證曲線的一致性,做了歸一化處理,也就是t
的取值都是在0-1之間。這麼說可能有點抽象,我們來看2個例子來對比就明白了,先看最簡單 Curves.linear
的實現。
class _Linear extends Curve { const _Linear._(); @override double transformInternal(double t) => t; }
超級簡單吧,直接返回 t,其實對應我們的數學的函數就是:
y = f(t) = t
對應的曲線就是一條斜線。也就是說在設定的動畫時間內,會完成從0-1的線性轉變,也就是變化是均勻的。線性這個很好理解,我們再來看一個減速曲線decelerate
的實現。
class _DecelerateCurve extends Curve { const _DecelerateCurve._(); @override double transformInternal(double t) { t = 1.0 - t; return 1.0 - t * t; } }
我們先看一下_DecelerateCurve 的計算表示式是什麼。
回憶一下我們高中物理學的勻減速運動,加速度為負(即減速)的距離計算公式:
上面的減速曲線其實就可以看做是初始速度是2,加速度也是2的減速運動。為什麼要是2這個值呢,這是因為 t 的取值範圍是0-1,這樣計算完的結果的取值範圍還是0-1。你肯定會問,為什麼要保證曲線的計算結果要是0-1?我們來假設計算結果不為0-1會發生什麼情況,比如我們要在螢幕上移動一個元件為60畫素。假設動畫曲線初始值不為0。那就意味著一開始的移動距離是跳變的。同樣的,如果結束值不為1.0,意味著在最後一個點的距離值不是60.0,那麼就意味著結束時需要從最後一個點跳到最終的60畫素的位置(動畫需要保證最終的移動距離是60畫素)這樣意味著動畫會出現跳變的效果,繪製曲線的話會是下面的樣子(綠色是正常的,紅線是異常的)。這樣的動畫體驗是很糟糕的!因此,這是一個關鍵點,如果你的自定義曲線的 transformInternal
方法的返回值範圍不是0-1,就意味著動畫會出現跳變,導致動畫缺幀的感覺。
image.png
有了這個基礎,我們就可以解釋動畫曲線的基本機制了,實際上就是在給定的動畫時間(Duration
)範圍內,完成元件的初始狀態到結束狀態的轉變,這個轉變是沿著設定的 Curve
類完成的,而其橫座標是0-1.0,曲線的初始值和結束值分別是0和1.0,而至於中間值是可以低於0或超過1的。我們可以想像是我們沿著設定的曲線運動,最終無論如何都會達到設定的目的地,而至於怎麼走,拐多少道彎,速度怎麼變化都是曲線控制的。但是,如果你的曲線初始值不為0或結束值不為1,就像是跳懸崖的那種感覺!
我們來一個正弦曲線的動畫驗證一下上面的說法。
class SineCurve extends Curve { final int count; const SineCurve({this.count = 1}) : assert(count > 0); @override double transformInternal(double t) { return sin(2 * count* pi * t); } }
count 引數用於控制週期,即達到目的地之前可以多來幾個來回。這裡我們發現,初始值是0,但是一個週期(2π)結束值也是0,這樣在動畫結束前會出現跳變的結果。來看一下範例程式碼,這個範例是讓圓形向下移動60畫素。
AnimatedContainer( decoration: BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.circular(30.0), ), transform: Matrix4.identity()..translate(0.0, up ? 60.0 : 0.0, 0.0), duration: Duration(milliseconds: 3000), curve: SineCurve(count: 1), child: ClipOval( child: Container( width: 60.0, height: 60.0, color: Colors.blue, ), ), )
執行效果如下,注意看最後一幀從0的位置直接跳到了60的位置。
跳動動畫
這個怎麼調呢,我們來看一下正弦曲線的樣子。
正弦曲線
如果我們要滿足0-1範圍的要求,那麼要往後再移動90度才能夠達到。但是,這樣還有個問題,這樣破壞了週期性,比如設定 count=2
的時候結果又不對了。我們來看一下規律,實際上只有第一個週期需要多移動90度(圖中箭頭指向的點),後面的都是按360度(即2π)為週期了。也就是角度其實是按2.5π,4.5π,6.5π……規律來的,對應的角度公式其實就是:
所以調整後的正弦曲線程式碼為:
class SineCurve extends Curve { final int count; const SineCurve({this.count = 1}) : assert(count > 0); @override double transformInternal(double t) { // 需要補償pi/2個角度,使得起始值是0.終止值是1,避免出現最後突然回到0 return sin(2 * (count + 0.25) * pi * t); } }
再看調整後的效果,是不是絲滑般地過渡了?
本篇介紹了 Flutter 動畫曲線類的原理和控制動畫的機制,實際上 Curve 類就是在指定的時間內,沿曲線完成從起點到終點的過渡。但是為了保證動畫平滑過渡,應該保證自定義曲線的transformInternal
方法返回值的起始值和結束值分別是0和1。
到此這篇關於詳解Android如何實現自定義的動畫曲線的文章就介紹到這了,更多相關Android動畫曲線內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45