天天看點

總結:一款Loading動畫的實作思路

感謝大家對前幾篇的支援,這一篇,我們一鼓作氣,把整個動畫完成。

慣例,為了友善第一次來的同學,我先貼一下動畫完成的效果圖:

總結:一款Loading動畫的實作思路

實作階段4時,我們用了一種處理問題的方式,大約是這樣的:

描述問題,直到足夠清晰,

把問題分解成一組小問題,

利用經驗處理可以解決的問題,

經驗無法解決的問題,我們去調研,調研結果會成為我們下次的經驗。

階段5中,我們再應用一下這個方式。

先來看一下階段5的效果圖,

慣例,前幾個階段的動畫我們用灰色快速表示,目前階段使用彩色慢速表示,如圖:

總結:一款Loading動畫的實作思路

階段5

看上去比階段4還要複雜,别急,我們來描述一下:

一開始圓是扁的,圓裡面有一條粗線,粗線的頂部和圓的頂部連在一起,

圓漸漸恢複原狀,同時粗線漸漸變長,連到了圓的底部,與此同時,粗線的某處出現了兩條線,分别向左下、右下延伸,漸漸連到了圓上。

很粗糙,但基本上描述出了這個階段。

上文中我加黑了部分文字,這些文字很有标志性

“一開始”,訓示出了本階段動畫的初始階段

“漸漸”,訓示出了動畫

“同時”、“分别”,訓示出了本階段中可以拆分出的子動畫

由此我們可以得到下面的描述:

初始:

圓:扁的

粗線:頂部和圓頂部連着,底部不連着

左下線:看不到

右下線:看不到

動畫:

圓:恢複到正常的形狀

粗線:變長

左下線:出現并變長

右下線:出現并變長

結束:

圓:正常的形狀

粗線:頂點、底點分别與圓的頂點、底點連着

左下線:起點在粗線上,終點在圓上

右下線:起點在粗線上,終點在圓上

和前面的描述相比,這個描述形式化了,雖然還比較粗略,但已經清晰的标明動畫被拆分成了4部分,各部分的初始及結束狀态也有了。

我們給4部分染上不同的顔色看一下:

總結:一款Loading動畫的實作思路

階段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:

總結:一款Loading動畫的實作思路

階段6_success

描述一下:

圓變色

對号漸漸出現(stroke)

失敗狀态,先是從階段3直接到階段6_fail:

總結:一款Loading動畫的實作思路

階段6_fail

歎号的上半部分漸漸出現(stroke)

歎号的下半部分漸漸出現(stroke)

然後從階段6_fail到階段7_fail:

總結:一款Loading動畫的實作思路

階段7_fail

歎号繞圓心左右晃幾下(rotate)

階段7_fail要簡單的說一下:

在階段6_fail中,歎号被拆分成了上下兩個layer,而在階段7_fail中兩者又是作為一個整體動的,我們要讓它們分别執行動畫麼?

不是的,一個獨立的動畫應該隻涉及一個對象,兩個layer有共同的superLayer,讓superLayer執行動畫就可以了,假如superLayer還有其他subLayer,不友善執行動畫,我們在兩個layer和superLayer中間插入一層專門執行動畫的layer就可以了。

到這裡,我們的動畫就完成了,完整代碼請移步GitHub上的OneLoadingAnimation工程。

在結束之前,我們簡單說一下階段1另一種思路(想看看階段1的同學請戳第一篇),這個思路更符合直覺,這個思路是受簡友YouXianMing在第一篇中的評論啟發,感謝。

先回憶一下階段1的樣子:

總結:一款Loading動畫的實作思路

階段1

圓從不完整漸漸變到完整(stroke)

圓在漸漸旋轉(rotate)

由此我們得出,一個圓同時執行stroke和rotate動畫就可以了,下面是示意代碼

  1. // 不完整的示意代碼
  2. - (void)doStep1 {
  3.     // 不用自定義layer了
  4.     self.arcToCircleLayer = [CAShapeLayer layer];
  5.     // stroke動畫
  6.     CABasicAnimation *ssAnima = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
  7.     CABasicAnimation *seAnima = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
  8.     // rotate動畫
  9.     CABasicAnimation *rotateAnima = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
  10.     // 同時執行
  11.     CAAnimationGroup *animation = [CAAnimationGroup animation];
  12.     animation.animations = @[ssAnima, seAnima, rotateAnima];
  13. }

複制代碼

是不是比第一篇的實作方式更清晰,

我們也可以看到,問題分解後,局部的優化也比較友善,

這部分的完整代碼我放到了工程的OneLoadingAnimationStep1Another目錄下。

有的同學還記得,我們這是一個簡化的版本,階段4中原動效中圓的不規則變形被我處理成了規則變形,

為了思路不被卡住,我選擇了暫時簡化,完成之後,我們可以再去優化。

為了彌補這個缺憾,我會開一個外篇,專門聊一下圓不規則變形的實作,歡迎大家到時來捧場;

另,有簡友在簡信中提到了Swift,是以我寫了一個Swift版的實作,放在了工程的OneLoadingAnimationCompleteSwift目錄下,由于我的Swift水準不夠,代碼裡還有坑,僅供參考。