時至今日,iOS 應用商店已經擁有超過了140萬 應用,讓你自己的應用脫穎而出確實是個不小的挑戰。不過,在你的應用掉入默默無聞的大黑洞之前,你擁有一個小小的機遇窗,它能幫你吸引使用者的注意。
想讓你的使用者喝彩尖叫,沒有比應用載入介面更好的地方 ,在這個地方,你可以新增一個討人喜歡的動畫來作為你登陸或者認證流程的先導。
在這個教程中,你將要學會如何利用先進的技術來建立一個流暢並且迷人的動畫。
開始吧!!
從這裡下載啟動專案,儲存在一個合適的路徑並用Xcode開啟。
開啟 HolderView.swift 。 在這個UIView 的子類中,你可以新增些子層(在Layers的下級目錄中可以找到),使之像上面的動畫一樣生動
OvalLayer.swift: 這是第一層,它從零尺寸擴充套件,然後會有一小段時間的搖擺
TriangleLayer.swift: 接下來的這個層TriangleLayer會在OvalLayer 搖擺的時候出現,當此檢視轉動時,OvalLayer 會縮小到零尺寸,並在TriangleLayer 中消失。
RectangleLayer.swift: 這個層是TriangleLayer 用於分類的視覺化容器
ArcLayer.swift: 這個層動畫特效填充在RectangleLayer 中,這和杯子裡填充了水(效果)非常相似
開啟OvalLayer.swift, 啟動專案已經包含了用於初始化這個層的程式碼和所有你會在動畫裡用到的Bezier path(物件)。你會看到expand(),wobble()和contract()方法都是空的。你可以通過參考這個指導書來填充這些方法。所有其他的 *Layer (以layer結尾)的檔案都用了相似的方式構建。
注意:如果你想要學習更多的Bezier paths,那就檢出我們系列指導書 Modern Core Graphics with Swift
最後,開啟ViewController.swift 檢視addHolderView()方法,這個方法新增了一個HolderView 作為一個子檢視,放到viewcontroller 檢視的中間。這個檢視將會放置所有的動畫。viewcontroller僅僅需要把它放到螢幕上,這個檢視將會照管好現行的動畫程式碼。
animateLabel() 是由類 HolderView 提供的代理回撥函式,此類中你會用你完成的動畫序列來填充。addButton()方法只是新增一個按鈕到檢視中,用於觸控和重啟動畫。
編譯並執行你的應用;你會看到一個空白螢幕。一個空白的畫布–這就是用於開始建立你的新動畫的完美的載體。在指導書的最後,你的應用會看起來是這樣的:
現在不需再費周折,我們開始吧
新增一個橢圓
這個動畫從一個橢圓開始,橢圓是從螢幕中間擴充套件到檢視,然後在周圍有點搖擺。
開啟 HolderView.swift ,在 HolderView 類的頂端附近宣告如下的常量
1 |
let ovalLayer = OvalLayer() |
現在在此類的底部新增如下方法
1 2 3 4 |
func addOval() { layer.addSublayer(ovalLayer) ovalLayer.expand() } |
這段程式碼首先新增了你上面建立的 OverLayer 的例項作為一個子層到檢視層,然後呼叫 expand() 方法,這是被切掉的需要你來填充的函式之一
來到 OvalLayer.swift 檔案,新增如下程式碼到 expand() 中:
1 2 3 4 5 6 7 8 9 |
func expand() { var expandAnimation: CABasicAnimation = CABasicAnimation(keyPath: "path") expandAnimation.fromValue = ovalPathSmall.CGPath expandAnimation.toValue = ovalPathLarge.CGPath expandAnimation.duration = animationDuration expandAnimation.fillMode = kCAFillModeForwards expandAnimation.removedOnCompletion = false addAnimation(expandAnimation, forKey: nil) } |
這個函式建立了一個 CABasicAnimation 的例項,這個例項用於改變橢圓從 ovalPathLarge.到 ovalPathSmall 的路徑。啟動專案為你提供了兩者的Bezier paths。
設定動畫的 removedOnCompletion 的值為 false,fillMode 的值為 KCAFillModeForwards ,使得當動畫結束的時候,橢圓保留它新的路徑。
最後,開啟 ViewController.swift ,在 view.addSubview(holderView) 下的 addHolderView() 方法中新增如下的線條
1 |
holderView.addOval() |
將 holdview 新增到 ViewController 的檢視中後,呼叫 addOval 方法來啟動動畫
構建並執行你的應用,你的動畫現在就會看起來像下面(圖例)
搖動橢圓
使用檢視中擴張的橢圓,下一步就是在橢圓的步調中設定一些反彈,使之搖擺起來
開啟 HolderView.swift,在此類的底部,新增下面的函式
1 2 3 |
func wobbleOval() { ovalLayer.wobble() } |
在 OvalLayer 中呼叫被切掉的方法 wobble().
現在開啟 OverLayer.swift,在 wobble() 中新增如下程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
func wobble() { // 1 var wobbleAnimation1: CABasicAnimation = CABasicAnimation(keyPath: "path") wobbleAnimation1.fromValue = ovalPathLarge.CGPath wobbleAnimation1.toValue = ovalPathSquishVertical.CGPath wobbleAnimation1.beginTime = 0.0 wobbleAnimation1.duration = animationDuration // 2 var wobbleAnimation2: CABasicAnimation = CABasicAnimation(keyPath: "path") wobbleAnimation2.fromValue = ovalPathSquishVertical.CGPath wobbleAnimation2.toValue = ovalPathSquishHorizontal.CGPath wobbleAnimation2.beginTime = wobbleAnimation1.beginTime + wobbleAnimation1.duration wobbleAnimation2.duration = animationDuration // 3 var wobbleAnimation3: CABasicAnimation = CABasicAnimation(keyPath: "path") wobbleAnimation3.fromValue = ovalPathSquishHorizontal.CGPath wobbleAnimation3.toValue = ovalPathSquishVertical.CGPath wobbleAnimation3.beginTime = wobbleAnimation2.beginTime + wobbleAnimation2.duration wobbleAnimation3.duration = animationDuration // 4 var wobbleAnimation4: CABasicAnimation = CABasicAnimation(keyPath: "path") wobbleAnimation4.fromValue = ovalPathSquishVertical.CGPath wobbleAnimation4.toValue = ovalPathLarge.CGPath wobbleAnimation4.beginTime = wobbleAnimation3.beginTime + wobbleAnimation3.duration wobbleAnimation4.duration = animationDuration // 5 var wobbleAnimationGroup: CAAnimationGroup = CAAnimationGroup() wobbleAnimationGroup.animations = [wobbleAnimation1, wobbleAnimation2, wobbleAnimation3, wobbleAnimation4] wobbleAnimationGroup.duration = wobbleAnimation4.beginTime + wobbleAnimation4.duration wobbleAnimationGroup.repeatCount = 2 addAnimation(wobbleAnimationGroup, forKey: nil) } |
程式碼真夠多的。但斷句還是很講究的。 接下來要做的是:
1. 從大路徑下降到被垂直壓扁的動畫
2. 從垂直壓扁變成水平和垂直都壓扁
3. 和垂直擠壓(動畫)切換
4. 回到大路徑結束動畫
5. 把你所有的動畫合併到CAAnimationGroup組,並把這個動畫組新增到你的 OvalLayout 中。
每一個隨後的動畫的 beginTime 都是其前一個動畫和動畫持續時間的 beginTime 總和。你重複動畫組兩次就會給你一種擺動出稍微拉長的感覺
儘管你現在擁有產生搖擺動畫的所有程式碼,你還是不能呼叫你的新動畫
我們回到 HolderView.swift,在 addOval() 結尾處新增如下程式碼
1 2 |
NSTimer.scheduledTimerWithTimeInterval(0.3, target: self, selector: "wobbleOval", userInfo: nil, repeats: false) |
在這裡,你建立了一個timer定時器,它會在OvalLayer已經結束擴張後呼叫 wobbleOval()
編譯並執行你的應用,檢查下你的新動畫。
這有點微妙,但那對一個真正的明快的動畫是一個重要的因素。你不再需要那些滿螢幕都是亂飛的東西了。
開始變身
是時候來電有趣的東西了。你將要把一個橢圓變身成為一個三角形。在使用者眼裡,這個轉變應該看上去無縫連線的。要做到這些,你會用到兩個相同顏色的分離的形狀。
開啟HolderView.swift,在HolderView類的頂端稍微靠近你早些時候新增的 OvalLayer 屬性的下面新增如下程式碼
1 |
let triangleLayer = TriangleLayer() |
這裡宣告瞭一個 TriangleLayer 類的常量,正如你在 OvalLayer 中做的一樣
現在,讓wobbleOval()方法看上去像這樣:
1 2 3 4 5 6 7 8 9 10 11 |
func wobbleOval() { // 1 layer.addSublayer(triangleLayer) // Add this line ovalLayer.wobble() // 2 // Add the code below NSTimer.scheduledTimerWithTimeInterval(0.9, target: self, selector: "drawAnimatedTriangle", userInfo: nil, repeats: false) } |
上面的程式碼做了如下這些事情:
- 這行(程式碼)新增了一個 TiangleLayer 例項,這個例項在稍早的時候作為HolderView層的子層已經被初始化過了。
- 正如你所知道的,因為這個搖擺動畫在1.8s的總間隔時間內執行兩次,所以在中間點啟動變形過程會是一個非常好的地方。因此,你要新增一個定時器timer,它在延遲0.9s之後執行drawAnimatedTriangle()
注意:找到動畫的正確的間隔或延遲需要反覆實驗,這也是一個好的動畫和一個極好的動畫區別。我鼓勵你去修補你的動畫,讓它們看上去完美。這可能要花點時間,但確是值得的。
接下來,在此類的底部新增如下的函式。
1 2 3 |
func drawAnimatedTriangle() { triangleLayer.animate() } |
這個方法會被你剛剛加入到 wobbleOval() 中的timer定時器呼叫。
現在開啟 TriangleLayer.swift,新增如下程式碼到 animate()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
func animate() { var triangleAnimationLeft: CABasicAnimation = CABasicAnimation(keyPath: "path") triangleAnimationLeft.fromValue = trianglePathSmall.CGPath triangleAnimationLeft.toValue = trianglePathLeftExtension.CGPath triangleAnimationLeft.beginTime = 0.0 triangleAnimationLeft.duration = 0.3 var triangleAnimationRight: CABasicAnimation = CABasicAnimation(keyPath: "path") triangleAnimationRight.fromValue = trianglePathLeftExtension.CGPath triangleAnimationRight.toValue = trianglePathRightExtension.CGPath triangleAnimationRight.beginTime = triangleAnimationLeft.beginTime + triangleAnimationLeft.duration triangleAnimationRight.duration = 0.25 var triangleAnimationTop: CABasicAnimation = CABasicAnimation(keyPath: "path") triangleAnimationTop.fromValue = trianglePathRightExtension.CGPath triangleAnimationTop.toValue = trianglePathTopExtension.CGPath triangleAnimationTop.beginTime = triangleAnimationRight.beginTime + triangleAnimationRight.duration triangleAnimationTop.duration = 0.20 var triangleAnimationGroup: CAAnimationGroup = CAAnimationGroup() triangleAnimationGroup.animations = [triangleAnimationLeft, triangleAnimationRight, triangleAnimationTop] triangleAnimationGroup.duration = triangleAnimationTop.beginTime + triangleAnimationTop.duration triangleAnimationGroup.fillMode = kCAFillModeForwards triangleAnimationGroup.removedOnCompletion = false addAnimation(triangleAnimationGroup, forKey: nil) } |
這段程式碼使三角層TriangleLayer的角一個挨一個的被彈拉成為橢圓 OvalLayer 層的擺動。Bezier path已經作為啟動工程的一部分被定義好。左邊的角首先執行,接下來是右邊的角,最後是上面的。你完成這個(動畫)需要藉助建立三個基於路徑的CABasicAnimation類的例項, CABasicAnimation 類已經被你新增到 CAAnimationGroup 組中,而組則被放到了 TriangleLayer 中。
構建並執行你的應用,看看當前動畫的狀態.
完成變形
為了完成變形過程,你需要在縮小OvalLayer橢圓層的同時,對 HolderView 旋轉360度,讓 TriangleLayer 三角層單獨隔離出來。
開啟 HolderView.swift,在 drawAnimatedTriangle(): 尾部新增如下程式碼
1 2 |
NSTimer.scheduledTimerWithTimeInterval(0.9, target: self, selector: "spinAndTransform", userInfo: nil, repeats: false) |
這裡設定了一個定時器timer,用於在三角形動畫結束後觸發。0.9s的時間還次用反覆實驗來確定
現在在這個類的底部新增如下的函式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
func spinAndTransform() { // 1 layer.anchorPoint = CGPointMake(0.5, 0.6) // 2 var rotationAnimation: CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z") rotationAnimation.toValue = CGFloat(M_PI * 2.0) rotationAnimation.duration = 0.45 rotationAnimation.removedOnCompletion = true layer.addAnimation(rotationAnimation, forKey: nil) // 3 ovalLayer.contract() } |
你之前建立的定時器新增了這段程式碼,定時器會在橢圓停止擺動並且三角行的角出現的時候呼叫這個函式。在這裡我們看下這個函式更詳細的(介紹)
- 更新層的錨點到略微靠近檢視中間的下方。這提供了一個看上去更加自然的旋轉。這是由於橢圓和三角形事實上比檢視中心在垂直方向上略微偏移。因此,如果檢視圍繞中心旋轉,橢圓和三角形可能會垂直方向移動
- 應用一個CABasicAnimation類來對層做360度旋轉,或者2*pi的弧度。旋轉是圍繞著Z軸,Z軸就是穿過螢幕,垂直於螢幕平面的軸
- 在OvalLayer中呼叫contract()來展示動畫,這個動畫會削減橢圓的尺寸直到消失
現在開啟 OvalLayer.swift,新增如下程式碼到 contract() 方法
1 2 3 4 5 6 7 8 9 |
func contract() { var contractAnimation: CABasicAnimation = CABasicAnimation(keyPath: "path") contractAnimation.fromValue = ovalPathLarge.CGPath contractAnimation.toValue = ovalPathSmall.CGPath contractAnimation.duration = animationDuration contractAnimation.fillMode = kCAFillModeForwards contractAnimation.removedOnCompletion = false addAnimation(contractAnimation, forKey: nil) } |
這段程式碼應用 CABasicAnimation 類,將 OvalLayer 設定它的初始路徑 ovalPathSmall。
構建並執行你的應用程式,當動畫完成的時候,只有三角形應該被留在螢幕上。
繪製容器
在下面這部分,你將要繪畫一個矩形容器,用於建立一個閉合圈。你將會用到 RectangleLayer 的描邊屬性。你需要這樣做兩次,將紅色和藍色都作為描邊色。
開啟 HolderView.swift, 像下面這樣宣告兩個 RectangularLayer 常量,(位置)就在你稍早時候 triangleLayer 屬性的下面
1 |
let redRectangleLayer = RectangleLayer()let blueRectangleLayer = RectangleLayer() |
接下來新增如下程式碼到 spinAndTransform(): 的尾部。
1 2 3 4 5 6 |
NSTimer.scheduledTimerWithTimeInterval(0.45, target: self, selector: "drawRedAnimatedRectangle", userInfo: nil, repeats: false) NSTimer.scheduledTimerWithTimeInterval(0.65, target: self, selector: "drawBlueAnimatedRectangle", userInfo: nil, repeats: false) |
這裡建立兩個定時器timer分別呼叫 drawRedAnimatedRectangle() 和 drawBlueAnimatedRectangle() 。旋轉動畫結束後,首先需要畫出矩形,當紅色矩形描邊繪畫接近完成的時候,藍色矩形描邊開始。
新增下面兩個方法頭此類的底部
1 2 3 4 5 6 7 8 9 |
func drawRedAnimatedRectangle() { layer.addSublayer(redRectangleLayer) redRectangleLayer.animateStrokeWithColor(Colors.red) } func drawBlueAnimatedRectangle() { layer.addSublayer(blueRectangleLayer) blueRectangleLayer.animateStrokeWithColor(Colors.blue) } |
一旦你新增矩形層 RectangleLayer 作為 HolderView 的子層,你就要呼叫 animateStrokeWithColor(color:) 並通過適當的顏色來繪畫出邊線。
現在開啟 RectangleLayer.swift, 像下面這樣填充 animateStrokeWithColor(color:)
1 2 3 4 5 6 7 8 |
func animateStrokeWithColor(color: UIColor) { strokeColor = color.CGColor var strokeAnimation: CABasicAnimation = CABasicAnimation(keyPath: "strokeEnd") strokeAnimation.fromValue = 0.0 strokeAnimation.toValue = 1.0 strokeAnimation.duration = 0.4 addAnimation(strokeAnimation, forKey: nil) } |
這段程式碼通過新增一個 CABasicAnimation物件,在 RectangleLayer 矩形層周圍繪畫了一個描邊。CAShapeLayer 的 strokeEnd 的 key(也就是keyPath)指示了在路徑周圍多遠的距離停止描邊。通過將這個屬性值從0調到1,你會產生一種路徑被從開始到結束都被繪畫的錯覺。 而從1到0,將會產生整個路徑被抹去的錯覺。
編譯並執行你的應用,檢視兩個描邊是如何看起來像他們構建的容器的。
填充容器
動畫的下一步就是填充容器。你要尋找到的效果就像是水填充到玻璃杯中。這是個非常棒的視覺特效,使之為一個大的飛濺特效
開啟 HolderView.swift,在 RectangleLayer 屬性稍靠下新增如下的常量
1 |
let arcLayer = ArcLayer() |
現在在drawBlueAnimatedRectangle():尾部新增如下的程式碼
1 2 |
NSTimer.scheduledTimerWithTimeInterval(0.40, target: self, selector: "drawArc", userInfo: nil, repeats: false) |
這(段程式碼)建立了一個定時器,用於當藍色 RectangleLayer 完成繪畫後呼叫 drawArc()
在類的結尾新增如下的函式
1 2 3 4 |
func drawArc() { layer.addSublayer(arcLayer) arcLayer.animate() } |
這段程式碼是在你動畫填充之前,新增了上面已經建立ArcLayer 的例項物件到HolderView 層。
開啟ArcLayer.swift 然後新增如下程式碼到animate():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
func animate() { var arcAnimationPre: CABasicAnimation = CABasicAnimation(keyPath: "path") arcAnimationPre.fromValue = arcPathPre.CGPath arcAnimationPre.toValue = arcPathStarting.CGPath arcAnimationPre.beginTime = 0.0 arcAnimationPre.duration = animationDuration var arcAnimationLow: CABasicAnimation = CABasicAnimation(keyPath: "path") arcAnimationLow.fromValue = arcPathStarting.CGPath arcAnimationLow.toValue = arcPathLow.CGPath arcAnimationLow.beginTime = arcAnimationPre.beginTime + arcAnimationPre.duration arcAnimationLow.duration = animationDuration var arcAnimationMid: CABasicAnimation = CABasicAnimation(keyPath: "path") arcAnimationMid.fromValue = arcPathLow.CGPath arcAnimationMid.toValue = arcPathMid.CGPath arcAnimationMid.beginTime = arcAnimationLow.beginTime + arcAnimationLow.duration arcAnimationMid.duration = animationDuration var arcAnimationHigh: CABasicAnimation = CABasicAnimation(keyPath: "path") arcAnimationHigh.fromValue = arcPathMid.CGPath arcAnimationHigh.toValue = arcPathHigh.CGPath arcAnimationHigh.beginTime = arcAnimationMid.beginTime + arcAnimationMid.duration arcAnimationHigh.duration = animationDuration var arcAnimationComplete: CABasicAnimation = CABasicAnimation(keyPath: "path") arcAnimationComplete.fromValue = arcPathHigh.CGPath arcAnimationComplete.toValue = arcPathComplete.CGPath arcAnimationComplete.beginTime = arcAnimationHigh.beginTime + arcAnimationHigh.duration arcAnimationComplete.duration = animationDuration var arcAnimationGroup: CAAnimationGroup = CAAnimationGroup() arcAnimationGroup.animations = [arcAnimationPre, arcAnimationLow, arcAnimationMid, arcAnimationHigh, arcAnimationComplete] arcAnimationGroup.duration = arcAnimationComplete.beginTime + arcAnimationComplete.duration arcAnimationGroup.fillMode = kCAFillModeForwards arcAnimationGroup.removedOnCompletion = false addAnimation(arcAnimationGroup, forKey: nil) } |
這個動畫和之前的搖擺動畫很相似。你建立了一個 CAAnimationGroup 動畫組,動畫組中包含五個基於路徑的 CABasicAnimation 例項物件。
每個路徑因高度遞增而有了稍微不同的弧,這些路徑也是啟動專案的一部分。最後,將 CAAnimationGroup 動畫組應用到層中,並使得動畫組在完成的時候不會被移除,因而當動畫完成的時候,它依然保留了自己的狀態。
構建並執行你的應用,看看這個神奇的展開吧。
完成動畫
剩下要做的就是擴充套件藍色的HolderView檢視來填充整個螢幕,並且新增一個UILabel作為一個logo新增到檢視中
開啟 HolderView.swift,在drawArc() 的結尾新增如下程式碼
1 2 |
NSTimer.scheduledTimerWithTimeInterval(0.90, target: self, selector: "expandView", userInfo: nil, repeats: false) |
這(段程式碼)建立了一個定時器,用於在 ArcLayer 填充到容器後呼叫 expandView()
現在,新增下面的函式到同一個類的底部:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
func expandView() { // 1 backgroundColor = Colors.blue // 2 frame = CGRectMake(frame.origin.x - blueRectangleLayer.lineWidth, frame.origin.y - blueRectangleLayer.lineWidth, frame.size.width + blueRectangleLayer.lineWidth * 2, frame.size.height + blueRectangleLayer.lineWidth * 2) // 3 layer.sublayers = nil // 4 UIView.animateWithDuration(0.3, delay: 0.0, options: UIViewAnimationOptions.CurveEaseInOut, animations: { self.frame = self.parentFrame }, completion: { finished in self.addLabel() }) } |
程式碼分析
- HolderView檢視的背景設定為藍色,和你填充到矩形的顏色匹配
- 幀擴充套件到你稍早時候新增的RectangleLayer矩形層的描邊寬度,
- 所有的子層都移除。現在沒有了橢圓,沒有了三角形,沒有了矩形圖層
- 新增動畫,並擴張HolderView填充螢幕,當動畫結束的時候,呼叫addLabel().
在類的底部,新增如下函式
1 2 3 |
func addLabel() { delegate?.animateLabel() } |
這裡只是簡單的呼叫檢視的代理函式,展示label標籤。
現在開啟ViewController.swift,新增如下程式碼到animateLabel():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
func animateLabel() { // 1 holderView.removeFromSuperview() view.backgroundColor = Colors.blue // 2 var label: UILabel = UILabel(frame: view.frame) label.textColor = Colors.white label.font = UIFont(name: "HelveticaNeue-Thin", size: 170.0) label.textAlignment = NSTextAlignment.Center label.text = "S" label.transform = CGAffineTransformScale(label.transform, 0.25, 0.25) view.addSubview(label) // 3 UIView.animateWithDuration(0.4, delay: 0.0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.1, options: UIViewAnimationOptions.CurveEaseInOut, animations: ({ label.transform = CGAffineTransformScale(label.transform, 4.0, 4.0) }), completion: { finished in self.addButton() }) } |
依次帶入各個註釋段
- 從檢視中移除HolderView ,並設定檢視的背景顏色為藍色。
- 建立一個文字為”S”的UIlabel標籤物件,用於展示logo,並新增到檢視。
- 標籤物件使用一個彈性動畫來使之伸縮。一旦動畫結束,呼叫 addButton() 來新增一個按鈕到檢視中,當按鈕按下的時候,重複動畫。
構建並執行應用程式,給自己點個贊,花個時間來欣賞自己構建的動畫吧。
下一步
你可以從這裡 下載 最終完整的專案。
這個指導書包含了相當多的不一樣的動畫技術,當這些動畫都堆疊在一起的時候,能夠創造一個相當複雜的載入動畫,這確實能夠讓你的應用在第一次被(使用者)執行的時候就眼前一亮。
從這裡,放鬆自由的玩玩不一樣的(動畫 的)定時和形狀,看看你能組裝成哪些很酷的動畫