今天中午無意中看到了賓士的一個發動機的宣傳視訊,一時手癢,想自己做一個動畫來實現一下發動機的動畫,當然做出來肯定和賓士的效果差很多:
不過這個動畫對於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;
複製程式碼