作者:鄭欽洪_
自己想好好學習動畫,在這個方向上發展,於是想總結一下自己學習這方面遇到的問題和收獲與大家分享,我會一直不斷的分享我學到的東西,會跟大家分享自己在學習動畫過程中搞懂的一些動畫原理,在下一篇會跟大家分享一下注水動畫的實現,希望得到大家的支持,也希望能夠一直把這個專題寫下去。
1. 圓弧動畫
經常我們會看到一個圓形加載動畫,圓弧大小總是在變化著的,例如芒果TV的加載動畫,眾乘巴士的加載動畫。這種動畫原理類似的還出現在一些一些儀表盤的動畫中,例如騰訊管家的測試WIFI速度功能,常常是一開始從0的位置或者是一個中間一個位置開始,做一段圓弧大小增縮的動畫。
通常這種動畫有以下幾個共同點:
一開始出現的時候圓弧都只是圓的一部分
圓弧不斷的向圓擴充,起始點和結束點也在不斷的變化接下來,我們就以以下這個加載動畫為例子,來說一下這類動畫的原理,分享一下我的拙見。GitHub傳送門
2. 拆分動畫
我們通過這張GIF高清無碼圖,看到一個圓弧從一開始慢慢從0變到0.5,在從0.5變到1的過程(這他媽不是廢話嗎?!),我們來看一下這兩個階段的動畫分別做了什麼事情:
0 -> 0.5 :起始點位置在0處,結束點從0慢慢變到0.5。
0.5 -> 1: 結束點從0.5處出發,用原來的速度走到1.0的位置,但是在這個階段開始的時候,起始點也動了,而且速度還比結束點運動的速度快,在結束點走到1.0的時候起始點已經追上了結束點的位置。
於是我們得出結論,一開始起始點不動,結束點動,當結束點走到一半的時候起始點發現不對勁了,也開始動,而且動的速度還比結束點快,在終點1.0處起始點追上了結束點 。
3. 實現
(1) CAShapeLayer
要實現這個動畫,首先我們得借助CAShapeLayer來做一個圓。在官方文檔的第一句話中,CAShapeLayer是這樣呗描述的:
/* *The shape layer draws a cubic Bezier spline in its coordinate space. * The spline is described using a CGPath object and may have both fill * and stroke components (in which case the stroke is composited over * the fill). The shape as a whole is composited between the layer's * contents and its first sublayer. */
CAShapeLayer 是在坐標系內繪制貝塞爾曲線的,通過繪制貝塞爾曲線,設置為shape的path,來繪制各種各樣的形狀,關於CAShapeLayer的介紹需要的可以自行簡書,上面有很詳細的介紹,我就不在這裡贅述。在這個Gif中,底部有一個灰色的圓,上面蓋著一個帶有動畫的紅色的圓,我們分別用CAShapeLayer來創建這兩個形狀。
/// 底部的灰色layer CAShapeLayer *bottomShapeLayer = [CAShapeLayer layer]; bottomShapeLayer.strokeColor = [UIColor colorWithRed:229/255.0 green:229/255.0 blue:229/255.0 alpha:1].CGColor; bottomShapeLayer.fillColor = [UIColor clearColor].CGColor; bottomShapeLayer.lineWidth = KShapelayerLineWidth; bottomShapeLayer.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(KShapeLayerMargin, 0, KShapeLayerWidth, KShapeLayerWidth) cornerRadius:KShapeLayerRadius].CGPath; [self.layer addSublayer:bottomShapeLayer]; /// 橘黃色的layer CAShapeLayer *ovalShapeLayer = [CAShapeLayer layer]; ovalShapeLayer.strokeColor = [UIColor colorWithRed:0.984 green:0.153 blue:0.039 alpha:1.000].CGColor; ovalShapeLayer.fillColor = [UIColor clearColor].CGColor; ovalShapeLayer.lineWidth = KShapelayerLineWidth; ovalShapeLayer.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(KShapeLayerMargin, 0,KShapeLayerWidth, KShapeLayerWidth) cornerRadius:KShapeLayerRadius].CGPath;
得到了上面圖片的圖形,接下來我們要在上面的圖片中增加動畫,達到我們想要的效果。
(2) 加入動畫
從一開始的分析中,我們可以知道,一開始起點位置不變,當結束點運行到0.5的時候以結束點2倍的速度運動,在結束點走一半的路徑時間內走完了整個圓。而結束點始終如一的在做勻速運動。
起始點:要麼一開始不動,一動就是人家的2倍速度,追上人家。
結束點:始終以相同的速度勻速運動。
這裡實現這個動畫有好幾種方法,其中一種是讓結束點先運動1半,然後這時候起始點開始運動,速度是結束點的2倍,結束點也在0.5的位置繼續以原來的速度運動,這種也是從gif圖上最直觀看到的動畫效果。但是在這裡我選擇了投機取巧,我讓起始點一開始也運動,並且是以結束點的速度的兩倍,在結束點一開始運動的時候也跟著運動。那麼這裡就奇怪了,一開始起始點就運動的話,並且速度比結束點大,是不可能走出一段圓形一半的圓弧的。這裡,我們就需要讓起始點辛苦點了,在相同的時間內要跑結束點運動路徑的兩倍,結束點在圓形上跑一圈,結束點要在相同的時間裡跑兩圈,並且一開始第一圈還必須在看不到的地方跑,也就是在起跑線之前跑一圈,這可苦了起始點了,但誰讓您老人家跑得太快了呢。
我們這裡設置strokeStart的初始值和結束值為 -1.0 和 1.0 ,而 strokeEnd 的初值和最終值為0.0和1.0 ,由於動畫時間相等,在strokeEnd的值為0.5的時候,strokeStart剛好從 -1.0到0.0,也就是我們一開始看到的效果,之後由於strokeStart的運動速度是strokeEnd的兩倍,所以能在接下來的路程中追趕上。
/// 起點動畫 CABasicAnimation * strokeStartAnimation = [CABasicAnimation animationWithKeyPath:@"strokeStart"]; strokeStartAnimation.fromValue = @(-1); strokeStartAnimation.toValue = @(1.0); /// 終點動畫 CABasicAnimation * strokeEndAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; strokeEndAnimation.fromValue = @(0.0); strokeEndAnimation.toValue = @(1.0); /// 組合動畫 CAAnimationGroup *animationGroup = [CAAnimationGroup animation]; animationGroup.animations = @[strokeStartAnimation, strokeEndAnimation]; animationGroup.duration = KAnimationDurationTime; animationGroup.repeatCount = CGFLOAT_MAX; animationGroup.fillMode = kCAFillModeForwards; animationGroup.removedOnCompletion = NO; [ovalShapeLayer addAnimation:animationGroup forKey:nil];
(3) 分割點效果
利用CAShapeLayer的lineDashPattern屬性,我們可以輕松的設置出分割點的效果,加上原來的動畫,就能做到我們想要的效果
ovalShapeLayer.lineDashPattern = @[@6,@3];
4. 後記
由於這一兩個月自己接了兩個外包項目,所以好久沒有寫簡書了,最近項目漸漸要結束了,作為學生黨最開心的就是看到下學期功課表只用上一天的課。接下來因為考完試時間比較充裕,會投入更多的時間在動畫的學習上,希望能將自己所學的、自己的一些拙見分享給大家,也希望得到大家的指點。