GacUI 動畫系統 (1)

很久以前 GacUI 就有一個動畫系統了,不過當時是為了讓C++手寫的Windows 7皮膚的按鈕具有動畫功能而做的。現在終於要實現用XML+Workflow來寫動畫了,所以那個架構不是很合適,於是我就開始重構。

重構的內容很簡單。以前一個動畫介面直接添加進GuiWindow裡面,但是XML創建的對象也有可能是皮膚和控制項,所以一個動畫介面只能添加進GuiInstanceRootObject里。但是timer實際上還在GuiWindow里,於是就會有把控制項從窗口裡面那開之後,因為失去了timer要暫停,然後為了節省timer廣播的資源,還要有一系列的其他動作等等。

這個設計其實很簡單,從GuiInstanceRootObject::AddAnimation點進去,瀏覽一下,就差不多了。

總的來說就是,凡是有動畫的root被添加進窗口,或者已經添加進窗口活著本身就是窗口的root被添加了第一個動畫之後,就要掛個callback在timer裡面。這個對象屬於root和timer共享。

  • 當動畫播完了,或者root被拿走的時候,root就往callback裡面記一下然後脫離關係,下次timer調用callback的時候就會知道他已經完蛋了,然後自己從列表裡刪除。
  • 如果窗口要被析構了,timer會直接把callback幹掉,callback自然就會隨著root的析構而洗頭了。
  • 如果一個窗口一個動畫也沒有,那麼timer裡面自然沒有任何callback,非常節省資源。

完美!

於是我寫了一個test case試了一下,大概長下面這個樣子。等整套動畫做完了之後,這些東西最終將會出現在BlackSkin裡面,還有controls裡面多一個例子。在我計劃中的動畫,

  • 有一部分可以用XML來完成(譬如說定義一個漸變的動畫,沒有理由要你寫代碼)
  • 有一部分可以你自己通過IGuiAnimation::CreateAnimation來創建
  • 最後一部分可以通過coroutine的方法把若干個小動畫拼接起來。拼接的結果當然也是一個IGuiAnimation,但是它可以被隨時暫停和恢復,但是你寫的時候只要定義動畫A接在B和C後面,D延遲多少同時執行這樣的直觀的函數調用。coroutine真是好用啊!

最後來看第一個例子,現在才剛剛重構,所以暫時只支持IGuiAnimation::CreateAnimaion。這個函數有兩個重載,分別用來做有限動畫和無限動畫。

<Resource> <Instance name="MainWindowResource"> <Instance ref.Class="demo::MainWindow" xmlns:demo="demo::*"> <ref.Members> <![CDATA[ @cpp:Private func CreateCircleAnimation(container: GuiBoundsComposition*, ball: GuiBoundsComposition*): IGuiAnimation^ { return IGuiAnimation::CreateAnimation( func (time: UInt64): void { var circleTime = (cast double (time % (cast UInt64 1600))) / 1600; var angle = circleTime * Math::Pi() * 2; var sin = Math::Sin(angle); var cos = Math::Cos(angle); var cx = (container.Bounds.x2 - container.Bounds.x1) / 2; var cy = (container.Bounds.y2 - container.Bounds.y1) / 2; var radiusBall = (ball.Bounds.x2 - ball.Bounds.x1) / 2; var radiusOrbit = Math::Min(cx, cy) - radiusBall; var x = cast int Math::Round(cos * radiusOrbit + cx); var y = cast int Math::Round(sin * radiusOrbit + cx); ball.AlignmentToParent = {left:(x - radiusBall) top:(y - radiusBall) right:-1 bottom:-1}; }); } ]]> </ref.Members> <ref.Ctor> <![CDATA[ { self.AddAnimation(CreateCircleAnimation(containerMA, ballMA)); } ]]> </ref.Ctor> <Window ref.Name="self" Text="GacUI XML資源臨時測試" ClientSize="x:640 y:480"> <att.BoundsComposition-set PreferredMinSize="x:640 y:480"/> <Tab> <att.BoundsComposition-set AlignmentToParent="left:5 top:5 right:5 bottom:5"/> <att.Pages> <TabPage Text="Manual Animation"> <BoundsComposition ref.Name="containerMA" AlignmentToParent="left:10 top:10 right:-1 bottom:-1" PreferredMinSize="x:200 y:200"> <SolidBackground Color="#FFC929"/> <BoundsComposition ref.Name="ballMA" PreferredMinSize="x:16 y:16"> <SolidBackground Shape="shapeType:Ellipse" Color="#3F48CC"/> </BoundsComposition> </BoundsComposition> </TabPage> <TabPage Text="Gradient Animation"> </TabPage> <TabPage Text="Complex Animation"> </TabPage> </att.Pages> </Tab> </Window> </Instance> </Instance></Resource>

效果很簡單:

接著馬上就要實現coroutine動畫了!


推薦閱讀:

簡化深度學習實踐流程:新鮮出爐的TensorFlow項目模板來了
C++中關於跨平台中子線程式控制制的一些心得(2):用於線程的同步的Async容器
使用subst命令快速跳轉到工作間
面向新手的雜談:Flyweight

TAG:編程 | 動畫 | 圖形用戶界面 |