欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Android如何實(shí)現(xiàn)自定義的動(dòng)畫(huà)曲線(xiàn)

 更新時(shí)間:2022年04月11日 08:41:45   作者:島上碼農(nóng)  
最近在寫(xiě)動(dòng)畫(huà)相關(guān)的篇章,經(jīng)常會(huì)用到 Curve 這個(gè)動(dòng)畫(huà)曲線(xiàn)類(lèi),那這個(gè)類(lèi)到底怎么實(shí)現(xiàn)的?如果想自己來(lái)一個(gè)自定義的動(dòng)畫(huà)曲線(xiàn)該怎么弄?本文將為大家詳細(xì)解答

前言

最近在寫(xiě)動(dòng)畫(huà)相關(guān)的篇章,經(jīng)常會(huì)用到 Curve 這個(gè)動(dòng)畫(huà)曲線(xiàn)類(lèi),那這個(gè)類(lèi)到底怎么實(shí)現(xiàn)的?如果想自己來(lái)一個(gè)自定義的動(dòng)畫(huà)曲線(xiàn)該怎么弄?本篇我們就來(lái)一探究竟。

曲線(xiàn)

Curve 類(lèi)定義

查看源碼, Curve 類(lèi)定義如下:

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);
}

看上去好像沒(méi)定義什么, 實(shí)際這里只是做了兩個(gè)處理,一個(gè)是明確的數(shù)據(jù)類(lèi)型為 double,另一個(gè)是對(duì) transform 做了重載,也只是對(duì)參數(shù) t 做了特殊處理,保證參數(shù) t 的范圍在0-1之間,且起點(diǎn)值0.0和終點(diǎn)值1.0不被轉(zhuǎn)換函數(shù)轉(zhuǎn)換。主要定義在上一層的ParametricCurve。文檔是建議子類(lèi)重載transformInternal方法,那我們就繼續(xù)往上看ParametricCurve這個(gè)類(lèi)的實(shí)現(xiàn),代碼如下:

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');
}

可以看到,實(shí)際上 transform 方法除了做參數(shù)合法性驗(yàn)證以外,其實(shí)就是調(diào)用了transformInternal方法,因此子類(lèi)必須要實(shí)現(xiàn)該方法,否則會(huì)拋出UnimplementedError異常。

實(shí)例解析

上面的源碼可以看到,關(guān)鍵在于參數(shù) t。這個(gè)參數(shù) t 代表什么呢?注釋里說(shuō)的是:

Returns the value of the curve at point t. — 返回 t 點(diǎn)的曲線(xiàn)對(duì)應(yīng)的值。

因此 t 可以認(rèn)為是曲線(xiàn)的橫坐標(biāo),而為了保證曲線(xiàn)的一致性,做了歸一化處理,也就是t的取值都是在0-1之間。這么說(shuō)可能有點(diǎn)抽象,我們來(lái)看2個(gè)例子來(lái)對(duì)比就明白了,先看最簡(jiǎn)單 Curves.linear 的實(shí)現(xiàn)。

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

??@override
??double?transformInternal(double?t)?=>?t;
}

超級(jí)簡(jiǎn)單吧,直接返回 t,其實(shí)對(duì)應(yīng)我們的數(shù)學(xué)的函數(shù)就是:

y?=?f(t)?=?t

對(duì)應(yīng)的曲線(xiàn)就是一條斜線(xiàn)。也就是說(shuō)在設(shè)定的動(dòng)畫(huà)時(shí)間內(nèi),會(huì)完成從0-1的線(xiàn)性轉(zhuǎn)變,也就是變化是均勻的。線(xiàn)性這個(gè)很好理解,我們?cè)賮?lái)看一個(gè)減速曲線(xiàn)decelerate的實(shí)現(xiàn)。

class?_DecelerateCurve?extends?Curve?{
??const?_DecelerateCurve._();

??@override
??double?transformInternal(double?t)?{
????t?=?1.0?-?t;
????return?1.0?-?t?*?t;
??}
}

我們先看一下_DecelerateCurve 的計(jì)算表達(dá)式是什么。

回憶一下我們高中物理學(xué)的勻減速運(yùn)動(dòng),加速度為負(fù)(即減速)的距離計(jì)算公式:

上面的減速曲線(xiàn)其實(shí)就可以看做是初始速度是2,加速度也是2的減速運(yùn)動(dòng)。為什么要是2這個(gè)值呢,這是因?yàn)?t 的取值范圍是0-1,這樣計(jì)算完的結(jié)果的取值范圍還是0-1。你肯定會(huì)問(wèn),為什么要保證曲線(xiàn)的計(jì)算結(jié)果要是0-1?我們來(lái)假設(shè)計(jì)算結(jié)果不為0-1會(huì)發(fā)生什么情況,比如我們要在屏幕上移動(dòng)一個(gè)組件為60像素。假設(shè)動(dòng)畫(huà)曲線(xiàn)初始值不為0。那就意味著一開(kāi)始的移動(dòng)距離是跳變的。同樣的,如果結(jié)束值不為1.0,意味著在最后一個(gè)點(diǎn)的距離值不是60.0,那么就意味著結(jié)束時(shí)需要從最后一個(gè)點(diǎn)跳到最終的60像素的位置(動(dòng)畫(huà)需要保證最終的移動(dòng)距離是60像素)這樣意味著動(dòng)畫(huà)會(huì)出現(xiàn)跳變的效果,繪制曲線(xiàn)的話(huà)會(huì)是下面的樣子(綠色是正常的,紅線(xiàn)是異常的)。這樣的動(dòng)畫(huà)體驗(yàn)是很糟糕的!因此,這是一個(gè)關(guān)鍵點(diǎn),如果你的自定義曲線(xiàn)的 transformInternal 方法的返回值范圍不是0-1,就意味著動(dòng)畫(huà)會(huì)出現(xiàn)跳變,導(dǎo)致動(dòng)畫(huà)缺幀的感覺(jué)。

image.png

有了這個(gè)基礎(chǔ),我們就可以解釋動(dòng)畫(huà)曲線(xiàn)的基本機(jī)制了,實(shí)際上就是在給定的動(dòng)畫(huà)時(shí)間(Duration)范圍內(nèi),完成組件的初始狀態(tài)到結(jié)束狀態(tài)的轉(zhuǎn)變,這個(gè)轉(zhuǎn)變是沿著設(shè)定的 Curve 類(lèi)完成的,而其橫坐標(biāo)是0-1.0,曲線(xiàn)的初始值和結(jié)束值分別是0和1.0,而至于中間值是可以低于0或超過(guò)1的。我們可以想像是我們沿著設(shè)定的曲線(xiàn)運(yùn)動(dòng),最終無(wú)論如何都會(huì)達(dá)到設(shè)定的目的地,而至于怎么走,拐多少道彎,速度怎么變化都是曲線(xiàn)控制的。但是,如果你的曲線(xiàn)初始值不為0或結(jié)束值不為1,就像是跳懸崖的那種感覺(jué)!

正弦動(dòng)畫(huà)曲線(xiàn)

我們來(lái)一個(gè)正弦曲線(xiàn)的動(dòng)畫(huà)驗(yàn)證一下上面的說(shuō)法。

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 參數(shù)用于控制周期,即達(dá)到目的地之前可以多來(lái)幾個(gè)來(lái)回。這里我們發(fā)現(xiàn),初始值是0,但是一個(gè)周期(2π)結(jié)束值也是0,這樣在動(dòng)畫(huà)結(jié)束前會(huì)出現(xiàn)跳變的結(jié)果。來(lái)看一下示例代碼,這個(gè)示例是讓圓形向下移動(dòng)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,
????),
??),
)

運(yùn)行效果如下,注意看最后一幀從0的位置直接跳到了60的位置。

跳動(dòng)動(dòng)畫(huà)

這個(gè)怎么調(diào)呢,我們來(lái)看一下正弦曲線(xiàn)的樣子。

正弦曲線(xiàn)

如果我們要滿(mǎn)足0-1范圍的要求,那么要往后再移動(dòng)90度才能夠達(dá)到。但是,這樣還有個(gè)問(wèn)題,這樣破壞了周期性,比如設(shè)置 count=2的時(shí)候結(jié)果又不對(duì)了。我們來(lái)看一下規(guī)律,實(shí)際上只有第一個(gè)周期需要多移動(dòng)90度(圖中箭頭指向的點(diǎn)),后面的都是按360度(即2π)為周期了。也就是角度其實(shí)是按2.5π,4.5π,6.5π……規(guī)律來(lái)的,對(duì)應(yīng)的角度公式其實(shí)就是:

所以調(diào)整后的正弦曲線(xiàn)代碼為:

class?SineCurve?extends?Curve?{
??final?int?count;
??const?SineCurve({this.count?=?1})?:?assert(count?>?0);

??@override
??double?transformInternal(double?t)?{
????//?需要補(bǔ)償pi/2個(gè)角度,使得起始值是0.終止值是1,避免出現(xiàn)最后突然回到0
????return?sin(2?*?(count?+?0.25)?*?pi?*?t);
??}
}

再看調(diào)整后的效果,是不是絲滑般地過(guò)渡了?

總結(jié)

本篇介紹了 Flutter 動(dòng)畫(huà)曲線(xiàn)類(lèi)的原理和控制動(dòng)畫(huà)的機(jī)制,實(shí)際上 Curve 類(lèi)就是在指定的時(shí)間內(nèi),沿曲線(xiàn)完成從起點(diǎn)到終點(diǎn)的過(guò)渡。但是為了保證動(dòng)畫(huà)平滑過(guò)渡,應(yīng)該保證自定義曲線(xiàn)的transformInternal方法返回值的起始值和結(jié)束值分別是0和1。

到此這篇關(guān)于詳解Android如何實(shí)現(xiàn)自定義的動(dòng)畫(huà)曲線(xiàn)的文章就介紹到這了,更多相關(guān)Android動(dòng)畫(huà)曲線(xiàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論