Flutter動(dòng)畫(huà)實(shí)例——4缸發(fā)動(dòng)機(jī)動(dòng)畫(huà)

今天中午無(wú)意中看到了奔馳的一個(gè)發(fā)動(dòng)機(jī)的宣傳視頻,一時(shí)手癢,想自己做一個(gè)動(dòng)畫(huà)來(lái)實(shí)現(xiàn)一下發(fā)動(dòng)機(jī)的動(dòng)畫(huà),當(dāng)然做出來(lái)肯定和奔馳的效果差很多:

不過(guò)這個(gè)動(dòng)畫(huà)對(duì)于app來(lái)說(shuō),也算是稍微些復(fù)雜的了,具體代碼就不介紹了,大家可以去GitHub看源碼,下面給大家介紹一下原理吧

結(jié)構(gòu)拆分

根據(jù)動(dòng)畫(huà)的內(nèi)容,需要把它分成幾部分:

  • 發(fā)動(dòng)機(jī)上蓋(灰色)
  • 氣缸和連桿(紅色)
  • 曲軸(下面的灰色)

其中的動(dòng)畫(huà),其實(shí)只涉及氣缸,左側(cè)的發(fā)動(dòng)機(jī)主視圖是氣缸上下運(yùn)動(dòng),右側(cè)的發(fā)動(dòng)機(jī)側(cè)視圖是氣缸的上下運(yùn)動(dòng)外加連桿的左右旋轉(zhuǎn)

布局

拆分了結(jié)構(gòu),下面就來(lái)布局,這里面大量使用了Container

這么多部件疊在一起,所以很直觀的就想到了Stack Widget,創(chuàng)建一個(gè)容器Container,然后child是Stack,上面說(shuō)的各個(gè)部件,都放在Stack里,并通過(guò)Position來(lái)指定位置

發(fā)動(dòng)機(jī)上蓋

發(fā)動(dòng)機(jī)上蓋是個(gè)長(zhǎng)方形Container,通過(guò)decoration設(shè)置了上部的兩個(gè)圓角,以及灰色的背景色。還有就是需要在上蓋上,掏出四個(gè)空間,來(lái)放置氣缸。

氣缸和連桿

氣缸和連桿,使用的是一個(gè)Column來(lái)封起來(lái)幾個(gè)Container,氣缸用了5個(gè)Container來(lái)實(shí)現(xiàn)活塞環(huán)的效果,連桿細(xì)長(zhǎng),并且底部有圓角
動(dòng)畫(huà)我們下一節(jié)說(shuō)

曲軸

曲軸分成幾部分:曲軸主軸,每個(gè)活塞拉桿的連接部分,連接部分包括兩個(gè)片狀Container和中間的掏空部分(掏空其實(shí)就是設(shè)置成背景顏色,看起來(lái)是掏空的)

動(dòng)畫(huà)

這里只有氣缸和連桿需要有動(dòng)畫(huà),實(shí)現(xiàn)了一個(gè)氣缸的動(dòng)畫(huà),那四個(gè)氣缸也差不多了

活塞的上下運(yùn)動(dòng)很簡(jiǎn)單,運(yùn)用補(bǔ)間動(dòng)畫(huà)Tween,讓他的值從0~2*PI之間變化,通過(guò)sin函數(shù),就可以實(shí)現(xiàn)活塞的往復(fù)運(yùn)動(dòng)

top: ShellHeight -
          CylinderHeight -
          CrankshaftRadius -
          CrankshaftRadius * sin(positionTween.evaluate(animation)),

而且使用sin函數(shù),因?yàn)椴皇蔷€性的,所以也實(shí)現(xiàn)了類似ease in out的效果。

右側(cè)發(fā)動(dòng)機(jī)側(cè)視圖的連桿旋轉(zhuǎn)著實(shí)費(fèi)了些腦筋,倒不是動(dòng)畫(huà)困難,而是曲軸往復(fù)運(yùn)動(dòng),與連桿的旋轉(zhuǎn)角度之間的關(guān)系,最后通過(guò)測(cè)試,發(fā)現(xiàn)從0~pi的時(shí)候,線性旋轉(zhuǎn),pi~2*pi的時(shí)候,三角函數(shù)旋轉(zhuǎn),擬合度比較高,至于旋轉(zhuǎn)的方法,使用Transform Widget

 Transform(
            transform:
                Matrix4.rotationZ(connectRodAngle(maxAngle, positionTween.evaluate(animation))),//旋轉(zhuǎn)角度
            alignment: Alignment.topCenter,
            child: Container(
              height: ConnectRodHeight,
              width: ConnectRodWidth,
              decoration: BoxDecoration(
                  color: CylinderColor,
                  borderRadius: BorderRadius.only(
                      bottomLeft: Radius.circular(ConnectRodWidth / 2),
                      bottomRight: Radius.circular(ConnectRodWidth / 2))),
            ),
          )

總結(jié)

整個(gè)實(shí)現(xiàn)其實(shí)很簡(jiǎn)單,想實(shí)現(xiàn)一個(gè)復(fù)雜動(dòng)畫(huà),思路整理很重要,不要被動(dòng)畫(huà)的表象繞進(jìn)去,另外,發(fā)動(dòng)機(jī)的各個(gè)尺寸和顏色,我都通過(guò)全局聲明,提高代碼的可維護(hù)性,所以現(xiàn)在如果我想把發(fā)動(dòng)機(jī)變成8缸,體積也變大,幾乎是10s以內(nèi)就能完成。

const EngineHeight = 170.0;
const ShellWidth = 200.0;
const ShellWidthLeft = 70.0;
const ShellHeight = 100.0;
const ShellPadding = 12.0;
const CylinderHeight = 18.0;
const CylinderWidth = 35.0;
const ConnectRodHeight = 60.0;
const ConnectRodWidth = 6.0;
const CrankshaftRadius = 20.0;
const CrankshaftRadiusWithBorder = CrankshaftRadius + 3;
const CrankshaftRodRadius = 5.0;
const CrankshaftFlatWidth = 4.0;
const CylinderCount = 4;
const ShellColor = Colors.grey;
const BackgroundColor = Colors.yellow;
const CylinderColor = Colors.red;
const CrankshaftColor = Colors.grey;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容