感謝大家對前幾篇的支援,這一篇,我們一鼓作氣,把整個動畫完成。
慣例,為了友善第一次來的同學,我先貼一下動畫完成的效果圖:

實作階段4時,我們用了一種處理問題的方式,大約是這樣的:
描述問題,直到足夠清晰,
把問題分解成一組小問題,
利用經驗處理可以解決的問題,
經驗無法解決的問題,我們去調研,調研結果會成為我們下次的經驗。
階段5中,我們再應用一下這個方式。
先來看一下階段5的效果圖,
慣例,前幾個階段的動畫我們用灰色快速表示,目前階段使用彩色慢速表示,如圖:
階段5
看上去比階段4還要複雜,别急,我們來描述一下:
一開始圓是扁的,圓裡面有一條粗線,粗線的頂部和圓的頂部連在一起,
圓漸漸恢複原狀,同時粗線漸漸變長,連到了圓的底部,與此同時,粗線的某處出現了兩條線,分别向左下、右下延伸,漸漸連到了圓上。
很粗糙,但基本上描述出了這個階段。
上文中我加黑了部分文字,這些文字很有标志性
“一開始”,訓示出了本階段動畫的初始階段
“漸漸”,訓示出了動畫
“同時”、“分别”,訓示出了本階段中可以拆分出的子動畫
由此我們可以得到下面的描述:
初始:
圓:扁的
粗線:頂部和圓頂部連着,底部不連着
左下線:看不到
右下線:看不到
動畫:
圓:恢複到正常的形狀
粗線:變長
左下線:出現并變長
右下線:出現并變長
結束:
圓:正常的形狀
粗線:頂點、底點分别與圓的頂點、底點連着
左下線:起點在粗線上,終點在圓上
右下線:起點在粗線上,終點在圓上
和前面的描述相比,這個描述形式化了,雖然還比較粗略,但已經清晰的标明動畫被拆分成了4部分,各部分的初始及結束狀态也有了。
我們給4部分染上不同的顔色看一下:
階段5多彩版
是不是比之前的清晰多了,
描述問題和分解問題,到這裡我們就完成了。
接下來就是思考動畫的方案了:
圓:階段4中是執行transform.scale.y動畫變扁了,本階段将transform恢複為CATransform3DIdentity就可以了;
粗線:階段4中是從無成長到一定長度,用的stroke方案(想了解stroke方案的同學請戳第二篇),這個階段就是繼續變長,沿用stroke方案就可以了;
左下線、右下線:本質是一樣,從無成長到一定長度,類比粗線可知,可以使用stroke方案;
這些方案都是以前的經驗,階段5可以不用調研了。
接下來就是找關鍵的節點值了:
對于一個動畫來講,關鍵的節點就是初始狀态和結束狀态,前幾篇中我們一起找過了關鍵的節點值,相信大家已經有感覺了,本文我們就不再找了。
我們一起來看一下階段5中特殊的地方:
前文中說到粗線時,我說的是繼續變長
繼續聽上去就有延續的意思,看上去, 粗線兩個階段的動畫可以合并成一個。
回憶一下,階段4中粗線的path起點是圓未變形時的頂點,終點是圓未變形時的圓心,動畫是從path起點逐漸stroke到path的終點,
結合階段5,我們可以将path的終點修改為圓的底點,将階段4的動畫修改為從path的起點stroke到path的1/2處,階段5的動畫是在階段4的基礎上繼續stroke到path的終點,這樣,兩個階段的動畫就合起來了。
有的同學可能會說,早知道是這樣,一開始就這樣寫就可以了。
這麼說的是有道理的,有的人習慣這樣,先分階段考慮,再整體看一下各階段,該合并的合并,該修改的修改,方案成形,最後寫代碼,這是個很好的方式。
然而對我來說,階段性的成就感很重要,每當我看到一個階段的動畫在我眼前動起來,感覺都很爽,是以我還是習慣于逐個階段的實作,有需要時重構前面的階段,隻要邏輯清晰,重構起來問題不大。
每個人都能找到最适合自己的方式,這本身就是一種樂趣。
好了,階段5我們聊了很多,後面的階段我們就簡要的說一下了。
從完整的效果圖可知,這個動畫是有成功和失敗兩個狀态的,是以我們分開來看。
成功狀态,從階段5到階段6_success:
階段6_success
描述一下:
圓變色
對号漸漸出現(stroke)
失敗狀态,先是從階段3直接到階段6_fail:
階段6_fail
歎号的上半部分漸漸出現(stroke)
歎号的下半部分漸漸出現(stroke)
然後從階段6_fail到階段7_fail:
階段7_fail
歎号繞圓心左右晃幾下(rotate)
階段7_fail要簡單的說一下:
在階段6_fail中,歎号被拆分成了上下兩個layer,而在階段7_fail中兩者又是作為一個整體動的,我們要讓它們分别執行動畫麼?
不是的,一個獨立的動畫應該隻涉及一個對象,兩個layer有共同的superLayer,讓superLayer執行動畫就可以了,假如superLayer還有其他subLayer,不友善執行動畫,我們在兩個layer和superLayer中間插入一層專門執行動畫的layer就可以了。
到這裡,我們的動畫就完成了,完整代碼請移步GitHub上的OneLoadingAnimation工程。
在結束之前,我們簡單說一下階段1另一種思路(想看看階段1的同學請戳第一篇),這個思路更符合直覺,這個思路是受簡友YouXianMing在第一篇中的評論啟發,感謝。
先回憶一下階段1的樣子:
階段1
圓從不完整漸漸變到完整(stroke)
圓在漸漸旋轉(rotate)
由此我們得出,一個圓同時執行stroke和rotate動畫就可以了,下面是示意代碼
- // 不完整的示意代碼
- - (void)doStep1 {
- // 不用自定義layer了
- self.arcToCircleLayer = [CAShapeLayer layer];
- // stroke動畫
- CABasicAnimation *ssAnima = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
- CABasicAnimation *seAnima = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
- // rotate動畫
- CABasicAnimation *rotateAnima = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
- // 同時執行
- CAAnimationGroup *animation = [CAAnimationGroup animation];
- animation.animations = @[ssAnima, seAnima, rotateAnima];
- }
複制代碼
是不是比第一篇的實作方式更清晰,
我們也可以看到,問題分解後,局部的優化也比較友善,
這部分的完整代碼我放到了工程的OneLoadingAnimationStep1Another目錄下。
有的同學還記得,我們這是一個簡化的版本,階段4中原動效中圓的不規則變形被我處理成了規則變形,
為了思路不被卡住,我選擇了暫時簡化,完成之後,我們可以再去優化。
為了彌補這個缺憾,我會開一個外篇,專門聊一下圓不規則變形的實作,歡迎大家到時來捧場;
另,有簡友在簡信中提到了Swift,是以我寫了一個Swift版的實作,放在了工程的OneLoadingAnimationCompleteSwift目錄下,由于我的Swift水準不夠,代碼裡還有坑,僅供參考。