Flutter動畫例項——4缸發動機動畫

Realank Liu發表於2018-10-11

Flutter動畫例項——4缸發動機動畫

今天中午無意中看到了賓士的一個發動機的宣傳視訊,一時手癢,想自己做一個動畫來實現一下發動機的動畫,當然做出來肯定和賓士的效果差很多:

Flutter動畫例項——4缸發動機動畫

不過這個動畫對於app來說,也算是稍微些複雜的了,具體程式碼就不介紹了,大家可以去GitHub看原始碼,下面給大家介紹一下原理吧

結構拆分

根據動畫的內容,需要把它分成幾部分:

  • 發動機上蓋(灰色)
  • 氣缸和連桿(紅色)
  • 曲軸(下面的灰色)

其中的動畫,其實只涉及氣缸,左側的發動機主檢視是氣缸上下運動,右側的發動機側檢視是氣缸的上下運動外加連桿的左右旋轉

佈局

拆分了結構,下面就來佈局,這裡面大量使用了Container

這麼多部件疊在一起,所以很直觀的就想到了Stack Widget,建立一個容器Container,然後child是Stack,上面說的各個部件,都放在Stack裡,並通過Position來指定位置

發動機上蓋

發動機上蓋是個長方形Container,通過decoration設定了上部的兩個圓角,以及灰色的背景色。還有就是需要在上蓋上,掏出四個空間,來放置氣缸。

氣缸和連桿

氣缸和連桿,使用的是一個Column來封起來幾個Container,氣缸用了5個Container來實現活塞環的效果,連桿細長,並且底部有圓角 動畫我們下一節說

曲軸

曲軸分成幾部分:曲軸主軸,每個活塞拉桿的連線部分,連線部分包括兩個片狀Container和中間的掏空部分(掏空其實就是設定成背景顏色,看起來是掏空的)

動畫

這裡只有氣缸和連桿需要有動畫,實現了一個氣缸的動畫,那四個氣缸也差不多了

活塞的上下運動很簡單,運用補間動畫Tween,讓他的值從0~2*PI之間變化,通過sin函式,就可以實現活塞的往復運動

top: ShellHeight -
          CylinderHeight -
          CrankshaftRadius -
          CrankshaftRadius * sin(positionTween.evaluate(animation)),
複製程式碼

而且使用sin函式,因為不是線性的,所以也實現了類似ease in out的效果。

右側發動機側檢視的連桿旋轉著實費了些腦筋,倒不是動畫困難,而是曲軸往復運動,與連桿的旋轉角度之間的關係,最後通過測試,發現從0~pi的時候,線性旋轉,pi~2*pi的時候,三角函式旋轉,擬合度比較高,至於旋轉的方法,使用Transform Widget

 Transform(
            transform:
                Matrix4.rotationZ(connectRodAngle(maxAngle, positionTween.evaluate(animation))),//旋轉角度
            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))),
            ),
          )
複製程式碼

總結

整個實現其實很簡單,想實現一個複雜動畫,思路整理很重要,不要被動畫的表象繞進去,另外,發動機的各個尺寸和顏色,我都通過全域性宣告,提高程式碼的可維護性,所以現在如果我想把發動機變成8缸,體積也變大,幾乎是10s以內就能完成。

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;
複製程式碼

相關文章