CAAnimation wiki

楊千嬅染了紅頭發IP屬地: 寧夏
0.166字數 1,042

CAAnimation是一個動畫抽象類,但是不要直接使用CAAnimation類,而是使用它的子類,如上圖所示展示的就是它的家族成員子類們,CAAnimation遵守CAMediaTimingCAAction協議,它是直接作用在CALayer上的,并非UIView上,動畫執行過程不在主線程上進行,所以不會阻塞主線程。先來看看CAnimation都定義了哪些屬性,方法。

+ animation初始化一個CAAnimation 對象

+ (instancetype)animation;---
**`CAAnimation`**是一個動畫抽象類,但是不要直接使用`CAAnimation`類,而是使用它的子類,如上圖所示展示的就是它的家族成員子類們,`CAAnimation`遵守`CAMediaTiming`和`CAAction`協議,它是直接作用在`CALayer`上的,并非`UIView`上,動畫執行過程不在主線程上進行,所以不會阻塞主線程。先來看看`CAnimation`都定義了哪些屬性,方法。


**`+ animation`**初始化一個CAAnimation 對象
  • (instancetype)animation;

`+defaultValueForKey:`  `- shouldArchiveValueForKey:`后者通過傳入一個關鍵字對動畫對象 進行序列化本地存儲,并且返回是否成功。然后使用相同的關鍵字調用前者來獲取這個持久化的對象。
  • (nullable id)defaultValueForKey:(NSString *)key;
  • (BOOL)shouldArchiveValueForKey:(NSString *)key;

 
**Timing Function (CAMediaTimingFunction)**
Timing Function會被用于變化起點到終點之間的插值計算,形象的說是Timing Function決定了動畫運行的節奏(Pacing),比如均勻變化(相同時間變化量相同),先快后慢,先慢后快還是不停變化速度。
Timing Function對應的類是`CAMediaTimingFunction`。

@property(nullable, strong) CAMediaTimingFunction *timingFunction;

`CAMediaTimingFunction`提供了兩種獲得時間函數的方法,一種是使用系統提供的五種時間函數,一種是自定義的時間函數。先來看第一種:
  • (id)functionWithName:(NSString *)name;
設置不同的`name`就能調用不同的系統提供的時間函數,`name`的可選值有:

/** Timing function names. **/

//線性 :勻速(速度不變,加速度為0)
A_EXTERN NSString * const kCAMediaTimingFunctionLinear
__OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);

//漸進:動畫緩慢進入,然后加速到達目的地
CA_EXTERN NSString * const kCAMediaTimingFunctionEaseIn
__OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);

//漸出:動畫全速進入,然后再減速到達目的地
CA_EXTERN NSString * const kCAMediaTimingFunctionEaseOut
__OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);

//漸進漸出:動畫緩慢進入,中間加速,然后減速到達目的地,這個是默認的動畫行為
CA_EXTERN NSString * const kCAMediaTimingFunctionEaseInEaseOut
__OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);

CA_EXTERN NSString * const kCAMediaTimingFunctionDefault
__OSX_AVAILABLE_STARTING (__MAC_10_6, __IPHONE_3_0);

我們來看下系統提供的這五種時間函數的具體效果:(Ease的意思是緩和)


* Ease in (`kCAMediaTimingFunctionEaseIn`
):

![](http://upload-images.jianshu.io/upload_images/276769-8e66fc4e8369fc4c.gif?imageMogr2/auto-orient/strip)

* Ease out (`kCAMediaTimingFunctionEaseOut`
):

![](http://upload-images.jianshu.io/upload_images/276769-97bc05022596c53a.gif?imageMogr2/auto-orient/strip)

* Ease in ease out (`kCAMediaTimingFunctionEaseInEaseOut`
):

![](http://upload-images.jianshu.io/upload_images/276769-25bc023663bf848e.gif?imageMogr2/auto-orient/strip)

* 默認 (kCAMediaTimingFunctionDefault
):

![](http://upload-images.jianshu.io/upload_images/276769-8de7f89e5091e4cd.gif?imageMogr2/auto-orient/strip)

**cubic-bezier() 時間函數**

你也可以使用`+ functionWithControlPoints::::`或者`- initWithControlPoints::::`自定義自己的時間函數(cubic-bezier() )。 

cubic-bezier() 定義了一條立方貝塞爾曲線(cubic Bézier curves)。這些曲線是連續的,一般用于動畫的平滑變換,也被成為緩動函數(easing functions)。

系統提供的四種時間函數的曲線圖(不包含默認的kCAMediaTimingFunctionDefault):

![](http://upload-images.jianshu.io/upload_images/276769-51a4a967a8e54563.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

縱軸x(t)的取值范圍是0.0-1.0,橫軸t的取值也是0.0-1.0,真實動畫的持續時間,值的變化范圍都是不定的,所以要按照比例進行換算,這個比較好理解就不細講了。
縱軸x(t)代表的是值改變的量,橫軸t代表時間。
值的改變量的意思就是當前時間的值與初始值的差值(是差值并不是當前時刻的值,因為原點是(0,0)),這個值的類型有很多下面會細講。
**形象一點的話你可以把x(t)看作當前位置距離起點的距離,那么速度 v = dx/dt , 加速度 a = dv/dt(求導)。**所以通過這個曲線就可以表現出:動畫在不同時刻的差值(與初始值相比較),值變化的速度,以及加速度。當v<0的時候就表示著x(t)的值是負增長,就好像汽車一直行駛的時候,開始倒車了。
  
這個圖理解了,再來看看 Bézier curve (貝塞爾曲線)
貝塞爾曲線于1962年,由法國工程師Pierre Bézier所廣泛發表,他運用貝塞爾曲線來為汽車的主體進行設計。貝塞爾曲線最初由Paul de Casteljau于1959年運用de Casteljau算法開發,以穩定數值的方法求出貝塞爾曲線。

`+ functionWithControlPoints::::`或者`- initWithControlPoints::::`構建的貝塞爾曲線是一條三次曲線,一條三次貝塞爾曲線如下圖所示需要四個點來定義,P0、P1、P2 和 P3。P0和P3是起點和終點,這兩個點被作為比例固定在坐標系上,橫軸為時間比例,縱軸為完成狀態。P0(0, 0)表示初始時間和初始狀態,P3是(1,1),表示終止時間和終止狀態。上述兩個方法的四個參數就是P1 , P2的坐標。

![](http://upload-images.jianshu.io/upload_images/276769-8afe20d291d93a77.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


三次貝塞爾曲線演示動畫,*t*在[0,1]區間
![Bézier_3](http://upload-images.jianshu.io/upload_images/276769-75f690bd96dcf78e.gif?imageMogr2/auto-orient/strip)

對貝塞爾曲線不理解的可以多查查相關資料,講了這么多,如果你想自己自定義一條貝塞爾曲線并看看實際效果可以利用http://cubic-bezier.com/ 這個網站自己動手實驗。


![演示](http://upload-images.jianshu.io/upload_images/276769-4a77b31566eaa5f3.gif?imageMogr2/auto-orient/strip)

  • (instancetype)functionWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y;
  • (instancetype)initWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y;
 

`removedOnCompletion`下面會利用`CAAnimation`的派生類`CABasicAnimation`進行講解 。

@property(getter=isRemovedOnCompletion) BOOL removedOnCompletion;


**`CAAnimationDelegate`**

//當動畫開始的時候被調用

  • (void)animationDidStart:(CAAnimation *)anim;

//當動畫結束的時候被調用

  • (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;

##CAPropertyAnimation

`CAPropertyAnimation`是`CAAnimation`的子類,`CAAnimation`定義的屬性,聲明的方法以及遵循的協議,`CAPropertyAnimation`也都全部適用。
`CAPropertyAnimation`有兩個子類`CABasicAnimation`和`CAKeyframeAnimation`,先來看下`CAPropertyAnimation`。

創建一個新的animation 對象,并且設置它的屬性`
keyPath`。

  • (instancetype)animationWithKeyPath:(nullable NSString *)path;

@property(nullable, copy) NSString *keyPath;

`keyPath`文檔是這么描述的** The key-path describing the property to be animated.**這個keyPath其實對應的是`layer`的屬性,設置了KeyPath就代表著,這個keyPath對應的`layer`屬性將會做動畫。
 關鍵來了,`layer`到底是有多少個屬性是可以用來做動畫的?你上網查是查不完全的,但是你可以查詢`CALayer `的**API**文檔:

/** Geometry and layer hierarchy properties. **/

/* The bounds of the layer. Defaults to CGRectZero. Animatable. */

@property CGRect bounds;

描述中只要包含`Animatable`的屬性就都可以用來做動畫。

繼續進行之前,先看一個layer易混淆的地方:
`layer`是通過樹形結構組織起來的,共有三種樹形結構
> * model  layer tree (模型層樹)
 > * presentation  tree (表示層樹)
 > * render tree (渲染層樹)

當改變layer的值的時候,model layer tree的值會馬上改變,通過render tree渲染,presentation tree以動畫的形式展現layer的某個屬性值的漸變過程。
`model layer tree`中的`layer`是我們通常意義說的`layer`,當我們修改layer的屬性的時候,就會立刻修改model layer tree。

view1.layer.anchorPoint = CGPointMake(0.6, 0.6);


 
refer tree(渲染層樹)是動畫的真實執行者,但是它是私有的,開發者不能調用。

@property(getter=isAdditive) BOOL additive;

默認是NO,當設置為YES的時候,使 Core Animation 在更新 presentation layer 之前將動畫的值添加到 model layer 中去。

詳細了解可以參考這篇文章:http://ronnqvi.st/multiple-animations/ 。

@property(getter=isCumulative) BOOL cumulative;


`cumulative`一般和`repeatCount`(動畫重復的次數)結合使用,當`cumulative`被設為YES時,下次動畫的值會在上一次動畫值的基礎上進行而不是重新回到初始狀態,可以看下面的兩個例子,`cumulative`分別被設置為YES或者NO。

![cumulative = YES](http://upload-images.jianshu.io/upload_images/276769-c7f9f9f814cafa91.gif?imageMogr2/auto-orient/strip)


![cumulative = NO](http://upload-images.jianshu.io/upload_images/276769-2dba8092054ff1b0.gif?imageMogr2/auto-orient/strip)


CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.x"];
animation.fromValue = @25;
animation.toValue = @100;
//動畫重復的次數
animation.repeatCount = 3;

//動畫的持續時間
animation.duration = 1;
animation.cumulative = NO;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
[_icon.layer addAnimation:animation forKey:@"animation"];


##CABasicAnimation

`CABasicAnimation`是`CAPropertyAnimation `的子類一般也是使用`animationWithKeyPath:`對`CABasicAnimation`進行實例化,并指定layer(帶有Animatable表示)的屬性作為關鍵路徑進行注冊。

###CAMediaTiming
`CAMediaTiming`是`CAAnimation`,`CALayer`所遵循的協議,上面沒提是因為感覺結合具體動畫的創建會更加直觀。`CAMediaTiming`協議定義了一段動畫內用來控制逝去時間的屬性的結合,可以精準的控制時間。

@property CFTimeInterval beginTime;

指定動畫的開始時間,如果動畫需要延遲幾秒開始應當這樣設置:`animation.beginTime = CACurrentMediaTime() +?`。

@property CFTimeInterval duration;

動畫`執行一次`的時長,無限重復可設置為`HUGE_VALF`。

@property float speed;

`speed`動畫執行一次的時間系數,默認是1.0,如果設置了`speed`的數值,那么動畫執行一次的時間就等于`duration /speed`,設置為0動畫將不會執行。

@property CFTimeInterval timeOffset;

`timeOffset`和`speed`不一樣,`timeOffset`不是一個時間系數,`timeOffset`是對時間進行偏移,計算出動畫在那個時刻的狀態然后開始執行。
** timeOffset = Duration > timeOffset ?  timeOffset : (Duration % timeOffset)**

有一點需要注意,不論timeOffset的值為多少,動畫從哪個狀態開始執行,動畫都會完整執行一次(比如timeOffset = 3.0 , Duration = 6.0;那么動畫將會從時間等于3秒的狀態開始執行,然后回到初始狀態執行 0 ~ 3的動畫)。


![timeOffset = 3.0 , Duration = 6.0](http://upload-images.jianshu.io/upload_images/276769-1520d91e16a584d9.gif?imageMogr2/auto-orient/strip)

//你可以自己測試一下,設置一下repeatCount看看效果,也可以設置一下speed的值
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.x"];
animation.duration = 6;
animation.fromValue = @25;
animation.toValue = @225;
animation.timeOffset = 3;
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeBackwards;
[_icon.layer addAnimation:self.animation2 forKey:@"animation2"];

@property float repeatCount;

動畫執行(重復)的次數,默認是0。

@property CFTimeInterval repeatDuration;

可以理解為動畫執行的總時長,`duration`是動畫執行一次的時長,兩者不一樣,默認值為0,不要和`repeatCount`共同使用。

@property BOOL autoreverses;

用來設置動畫是否有回放的效果,默認為NO。

@property(copy) NSString *fillMode;

fillModel:填充模式。有四個可選值,只有在`removeOnCompletion`設置為NO的時候才會生效。
`removeOnCompletion`:動畫結束時是否自動移除,如果設置為YES,那么你要手動給layer賦值,否則layer會自動回到動畫開始之前的狀態。
layer動畫運行的過程:其實我們給一個視圖添加 layer動畫時,真正移動并不是我們視圖本身,而是presentation layer 的一個緩存,動畫開始的時候presentation layer開始移動,原始layer隱藏,動畫結束時,presentation layer從屏幕上移除原始layer顯示,這就解釋了為什么我們的視圖在結束后又回到了原的狀態,因為它根本沒動過。

KCAFillModelRemoved 這個是默認值,也就是說動畫開始前和動畫結束后,動畫對layer都沒有影響,動畫結束后,layer會恢復到之前的狀態 , 動畫將在設置的beginTime開始執行(如果沒有設置beginTime,則動畫立即執行)。
![KCAFillModelRemoved](http://upload-images.jianshu.io/upload_images/276769-73a6454a3db7f103.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

KACFillModelForwards 當動畫結束后,layer會一直保持著動畫最后的狀態。

![KACFillModelForwards](http://upload-images.jianshu.io/upload_images/276769-2de1257bf05da36c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


 KCAFillModeBackwards 會立即執行動畫的第一幀,不論是否設置了beginTime屬性。


![KCAFillModeBackwards](http://upload-images.jianshu.io/upload_images/276769-5cc188ba945d85d1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

 KCAFillModeBoth  動畫加入開始之前,layer便處于動畫初始狀態,動畫結束后layer保持動畫最后的狀態。

![KCAFillModeBoth](http://upload-images.jianshu.io/upload_images/276769-599f559edd7f06c2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

以上是`CAMediaTiming `的內容,繼續看`CABasicAnimation `三個關于layer值的屬性。


@property(nullable, strong) id fromValue;


@property(nullable, strong) id toValue;


@property(nullable, strong) id byValue;


`fromValue`: KeyPath對應layer屬性 的初始值。
    `toValue`: keyPath對應layer屬性 的結束值。
伴隨動畫的進行,在`duration`的持續時間內,keyPath對應的layer屬性值從fromValue漸漸地變為toValue。
`byValue`表示動畫期間改變的值,以layer的`position.x`舉例,當`fromeValue = @100;` `byValue = @100;`就表示著layer的`position.x`初始值為100 ,動畫期間`position.x`改變的值是100,`position.x`從初始值100變為200。
`toValue`和`byValue`不要同時使用,作用沖突。


`CABasicAnimation`實踐

##CAKeyframeAnimation 
關鍵幀(keyframe)使我們能夠定義動畫中任意的一個點,然后讓Core Animation填充所謂的中間幀。
`CAKeyframeAnimation`與`CABasicAnimation`雖然都是`CAPropertyAnimation`的子類但是兩者還是有區別的:
`CABasicAnimation`:只能讓`keyPath`對應的`layer`的某個可動畫(Animatable)的屬性,從初始值(fromValue)轉變到另外一個數值(toValue)。
`CAKeyframeAnimation`:可以使用一個數組` NSArray *values`,保存這些數值(數值的個數不定),并使用數組`NSArray<NSNumber *> *keyTimes`來保存這些數值對應的時間點。

@property(nullable, copy) NSArray *values;

存放放關鍵幀(keyframe)的數組,動畫對象會在指定的時間(duration)內,依次顯示values數組中的每一個關鍵幀。

@property(nullable, copy) NSArray<NSNumber *> *keyTimes;

存放對應的關鍵幀指定的對應的時間點,存放的類型必須為`NSNumber`類型,取值范圍在0到0.1(會根據duration進行一個時間的換算),keyTimes里面每一個時間點對應數組values的每一幀,如果沒有設置keyTimes或者`KeyTimes = @[]`,那么各個關鍵幀的時間會被系統平分。
**需要注意的是`keyTimes`數組里的時間點,必須要后面的值大于前面的值,而且最好不要使用分數設置像這樣`@(1/7)`這樣`3/7`或者這樣`@(4/9)`因為你可能會得到一個除不盡的小數,動畫效果會出現問題,不會按照你的預期執行,這個已經測試**。

![keyframe](http://upload-images.jianshu.io/upload_images/276769-24f49d490299f282.gif?imageMogr2/auto-orient/strip)

CAKeyframeAnimation * keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position.x"];

keyframeAnimation.values = @[@(WIDTH/2 - 35) , @(WIDTH/2+35) , @(WIDTH/2 - 35) , @(WIDTH/2+35) , @(WIDTH/2 - 35),@(WIDTH/2+35), @(WIDTH/2 - 35),@(WIDTH/2)];

keyframeAnimation.keyTimes = @[@0, @(0.1), @(0.2), @(0.3),@(0.4),@(0.5) ,@(0.7),@1 ];

keyframeAnimation.duration = 3;

[icon.layer addAnimation:self.keyframeAnimation forKey:@"keyfra
meAnimation"];


@property(nullable) CGPathRef path;

`CAKeyframeAnimation`路徑動畫的路徑,但是一般不直接使用它,使用的是`UIBezierPath`,`UIBezierPath`是`CGPathRef`數據類型的封裝。

###UIBezierPath
`UIBezierPath`的作用就是創建直線,曲線,矩形圓弧或其它復雜的曲線 形狀。
`UIBezierPath`的使用場景有三種:
* 1 . 與`CAKeyAnimation`結合使用,作為動畫的路徑。
* 2 . 在方法`- (void)drawRect:(CGRect)rect`中畫圖使用。
* 3 . 與`CAShapeLayer`配合使用,貝塞爾曲線給`CAShapeLayer`提供路徑,`CAShapeLayer`在提供的路徑中進行渲染。

這里先看看它與`CAKeyAnimation`的結合使用。

UIBezierPath 相關類方法
  • (instancetype)bezierPathWithRect:(CGRect)rect;
繪制一個矩形的貝塞爾曲線


![bezierPathWithRect](http://upload-images.jianshu.io/upload_images/276769-17ac311958269662.gif?imageMogr2/auto-orient/strip)
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRect:CGRectMake(100, 100 , (WIDTH - 100) / 2, 200)];
CAKeyframeAnimation *keyFrameAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
keyFrameAnimation.duration = 2.0;
keyFrameAnimation.repeatCount = 3;
keyFrameAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
keyFrameAnimation.fillMode = kCAFillModeForwards;
[_icon.layer addAnimation:keyFrameAnimation forKey:@"keyFrameAnimation"];

  • (instancetype)bezierPathWithOvalInRect:(CGRect)rect;
繪制一個矩形的內切圓的貝塞爾曲線

![內切圓](http://upload-images.jianshu.io/upload_images/276769-72979c42987b8c37.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)



![內切圓.gif](http://upload-images.jianshu.io/upload_images/276769-9b19fa453b3e56af.gif?imageMogr2/auto-orient/strip)

UIBezierPath *bezierPath2 = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(100, 100, (WIDTH - 100)/2, 200)];

keyFrameAnimation.path = bezierPath2.CGPath;

[_icon.layer addAnimation:keyFrameAnimation forKey:@"keyFrameAnimation"];


  • (instancetype)bezierPathWithRoundedRect:(CGRect)rect
    cornerRadius:(CGFloat)cornerRadius;
繪制一個可以設置圓角的矩形貝塞爾曲線

![bezierPathWithRoundedRect:
cornerRadius:
](http://upload-images.jianshu.io/upload_images/276769-fb39196f7f521ea0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


![bezierPathWithRoundedRect:
cornerRadius:
](http://upload-images.jianshu.io/upload_images/276769-ef62bc07666a9087.gif?imageMogr2/auto-orient/strip)
CGRect frame = CGRectMake(WIDTH/2 - 100, 150, 200, 200);
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:frame cornerRadius:65];
keyFrameAnimation.path = bezierPath.CGPath;
[_icon.layer addAnimation:keyFrameAnimation forKey:@"keyFrameAnimation"];

  • (instancetype)bezierPathWithRoundedRect:(CGRect)rect
    byRoundingCorners:(UIRectCorner)corners
    cornerRadii:(CGSize)cornerRadii;
畫一個有**部分圓角**矩形的貝塞爾曲線
`rect`:矩形的frame
`corners`:一個枚舉值,用來指定矩形哪些角被設置為圓角,五個可選值,左上角、右上角、左下角、右下角、全部。

typedef NS_OPTIONS(NSUInteger, UIRectCorner) {
UIRectCornerTopLeft = 1 << 0,
UIRectCornerTopRight = 1 << 1,
UIRectCornerBottomLeft = 1 << 2,
UIRectCornerBottomRight = 1 << 3,
UIRectCornerAllCorners = ~0UL
};

cornerRadii:(CGSize)cornerRadii

***cornerRadii 是 CGSize 類型 , 你看其它的資料大部分都寫的是 這是用來設置橢圓半徑的,首先這不能叫半徑,要叫也是叫長軸(2a),短軸(2b),其次你設置height(高)是根本沒有效果的,只有width是有效果的,這個圓角其實就是半徑 = width 圓的一段圓弧 , 如下圖所示,你也可以自己做個測試,也可以自己做個路徑動畫***

![](http://upload-images.jianshu.io/upload_images/276769-c2b5d8fa8e7daa58.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
** 一句話總結就是height 是多少無所謂 , 類似與
layer.cornerRadius    **

  • (instancetype)bezierPathWithArcCenter:(CGPoint)center
    radius:(CGFloat)radius
    startAngle:(CGFloat)startAngle
    endAngle:(CGFloat)endAngle
    clockwise:(BOOL)clockwise;
自己定義一些參數畫一段圓弧。

center:圓弧的圓心坐標

radius:圓弧的半徑

startAngle:圓弧起始的弧度

endAngle:圓弧結束的弧度

clockwise:圓弧從起始弧度指向結束弧度的方向,YES是順時針方向,NO是逆時針方向

**需要注意的startAngel endAngel并不是讓你填入類似45度 , 30度這種數值,startAngel endAngel是弧度,圓一周的弧度數為2πr/r=2π,360°角=2π弧度,所以如果你想使用使用30度 , 90度這種數值請進行換算,代碼里面M_PI等同于π。**
![](http://upload-images.jianshu.io/upload_images/276769-c70d9b73779a2da4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

上圖是iOS devlop文檔的配圖,看下面我做的圖更好理解一點。

![](http://upload-images.jianshu.io/upload_images/276769-0992faecc25bbbf5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

下面你就可以自己畫一個圓弧看一下效果。
  • (instancetype)initWithFrame:(CGRect)frame
    {
    self = [super initWithFrame:frame];
    if (self) {
    self.backgroundColor = ColorWithHexValue(0x5EB35F);
    }
    return self;
    }

  • (void)drawRect:(CGRect)rect
    {
    //設置線的填充色
    [ColorWithHexValue(0xE9816D) setStroke];

    //新建一個bezier對象
    UIBezierPath bezierPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 100) radius:30 startAngle:0 endAngle:1.5M_PI clockwise:YES];

    //設置線寬度
    bezierPath.lineWidth = 1;
    //開始繪制
    [bezierPath stroke];
    }

圖像如下所示:


![](http://upload-images.jianshu.io/upload_images/276769-bf0a5e12ab3ab325.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

構建路徑Path的相關方法:

  • (void)moveToPoint:(CGPoint)point

改變當前點的位置到point點,你可以看作是設置一條Path的起點。

  • (void)addLineToPoint:(CGPoint)point
從當前點增加一條直線到該point,然后改變當前點把point作為當前點 。

-(void)addArcWithCenter:(CGPoint)center
radius:(CGFloat)radius
startAngle:(CGFloat)startAngle
endAngle:(CGFloat)endAngle
clockwise:(BOOL)clockwise

這個就跟上面說過的畫一段圓弧的方法是一樣的。

  • (void)addCurveToPoint:(CGPoint) controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2
構建一個三次的貝塞爾曲線。

  • (void)addCurveToPoint:(CGPoint) controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2
構建一段二次貝塞爾曲線。

貝塞爾曲線,上面時間函數的時候就已經說得比較清楚了,還想更深入的了解的話,維基百科寫的很清晰,講的也很清楚,有興趣的可以科學的查一下。

UIBezierPath 就先到這里結束了,通常情況的具體三種用法就是一開始介紹它的時候寫的,自己試試吧。
繼續回到`@interface CAKeyframeAnimation : CAPropertyAnimation`

`calculationMdel`計算模式,主要針對的動畫的沒一怔的內容都是一個坐標點的情況,也就是針對anchortPoint和position進行的動畫,當在平面坐標系中有多個離散的點的時候可以是離散的,也可以是直線相連后進行插值計算,還可以使用圓滑的曲線將他們相連后進行差著計算,calculationMdel提供如下幾種模式:

**插值**:在離散數據的基礎上補插連續函數,使得這條連續曲線通過全部給定的離散數據點.插值是離散函數逼近的重要方法,利用它通過函數在有限個點處的取值狀況,估算出函數在其他點處的近似值,插值:用來填充圖像變換時像素之間的空隙。

CA_EXTERN NSString * const kCAAnimationLinear
__OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);

CA_EXTERN NSString * const kCAAnimationDiscrete
__OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);

CA_EXTERN NSString * const kCAAnimationPaced
__OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);

CA_EXTERN NSString * const kCAAnimationCubic
__OSX_AVAILABLE_STARTING (__MAC_10_7, __IPHONE_4_0);

CA_EXTERN NSString * const kCAAnimationCubicPaced
__OSX_AVAILABLE_STARTING (__MAC_10_7, __IPHONE_4_0);


**kCAAnimationLinear**: calculationModel的默認值,表示當關鍵幀是坐標點的時候,關鍵這之間直接直線連接進行插值計算;

**kCAAnimationDiscrete**: 離散的,就是不進行插值計算,所有關鍵幀直接逐個進行顯示 ;

**kCAAnimationPaced**:使得動畫均勻進行,而不是按keyTimes設置的或者按關鍵幀平分時間,此時keyTimes和timeFunctions無效;

**kCAAnimationCubic**對關鍵幀為坐標點的關鍵幀進行圓滑曲線相連后插值計算,曲線的形狀可以通過`tensionValues`,`continuityValues`,`biasValues`來進行調整自定義,使得運動的軌跡變得圓滑;

**kCAAnimationCubicPaced**:這個是基于**kCAAnimationCubic**的,它的作用是使動畫運動變得均勻,就是系統時間內運動的距離相同,此時keyTimes以及timingFunctions也是無效的。

![calculationMdel.png](http://upload-images.jianshu.io/upload_images/276769-b3139a444759bf21.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


@property(nullable, copy) NSArray<NSNumber *> *tensionValues;
@property(nullable, copy) NSArray<NSNumber *> *continuityValues;
@property(nullable, copy) NSArray<NSNumber *> *biasValues;


這三個屬性是對每一幀的細節的設置,分別是張力值,持續性值,偏移值。

@property(nullable, copy) NSString *rotationMode;

**rotationModel** 有兩個可選值:

CA_EXTERN NSString * const kCAAnimationRotateAuto
CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);
CA_EXTERN NSString * const kCAAnimationRotateAutoReverse
CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);

這兩個都可以讓做動畫的時候沿著路徑的切線方向,但是也有稍微的不同,看下面的演示就清楚了。


![kCAAnimationRotateAuto.gif](http://upload-images.jianshu.io/upload_images/276769-173f59186228680a.gif?imageMogr2/auto-orient/strip)


![kCAAnimationRotateAutoReverse.gif](http://upload-images.jianshu.io/upload_images/276769-ab2af1575b4fe1f3.gif?imageMogr2/auto-orient/strip)
##@interface CASpringAnimation : CABasicAnimation

**CASpringAnimation**是**CABasicAnimation**的子類,也就是說**CABasicAnimation**定義的屬性它也都有,看看**CASpringAnimation**自己獨有的屬性有哪些。

***需要注意的是CASpringAnimation是iOS9才引入的動畫類,按目前APP的開發,一般很少有 只支持iOS9以上的,一般都會兼容iOS7,個人建議使用POP。***

@property CGFloat mass;

質量,影響圖層運動時的彈簧慣性,質量越大,動畫運動的幅度越大,彈簧拉伸壓縮的幅度越大,數值必須大于0,默認是1。


@property CGFloat stiffness;


彈簧的勁度系數(彈性系數),必須大于0,默認為100。它描述的是單位形變量時所產生彈力的大小,k值大,說明形變單位長度需要的力大,或者說彈簧"韌",勁度系數在數值上等于彈簧伸長(或縮短)單位長度的彈力。

@property CGFloat damping;

阻尼系數,必須大于或者等于0,默認為10。
**阻尼**是指任何振動系統在振動中,由于外界作用、摩擦力等)和/或系統本身固有的原因引起的振動幅度逐漸下降的特性,以及此一特性的量化表征。

在實際振動中,由于摩擦力總是存在的,所以振動系統最初所獲得的能量,在振動過程中因阻力不斷對系統做負功,使得系統的能量不斷減少,振動的強度逐漸減弱,振幅也就越來越小,以至于最后的停止振動,像這樣的因系統的力學能,由于摩擦及轉化成內能逐漸減少,振幅隨時間而減弱振動,稱為阻尼振動。





![一個有阻尼的彈簧振子振動示意圖](http://upload-images.jianshu.io/upload_images/276769-5d8867f525d87496.gif?imageMogr2/auto-orient/strip)



@property CGFloat initialVelocity;


連接到彈簧的對象得得初始速度,默認值是0,負值的時候,速度方向與運動方向相反。

@property(readonly) CFTimeInterval settlingDuration;

動畫的結算時間,從spring動畫開始到停止的一個估算時間,根據當前設置的動畫參數進行估算。


CASpringAnimation *springAnimation = [CASpringAnimation animationWithKeyPath:@"position.x"];
springAnimation.fromValue = @100;
springAnimation.toValue = @150;
springAnimation.mass = 100.0;
springAnimation.stiffness = 100.0;
springAnimation.damping = 100.0;
springAnimation.initialVelocity = 10;
springAnimation.repeatCount = 3;
springAnimation.fillMode = kCAFillModeRemoved;

[_icon.layer addAnimation:springAnimation forKey:@"springAnimation"];



![springAnimation](http://upload-images.jianshu.io/upload_images/276769-fc52fc9ccd146414.gif?imageMogr2/auto-orient/strip)

參考列表:
[https://objccn.io/issue-12-1/](https://objccn.io/issue-12-1/) 
https://developer.mozilla.org/zh-CN/docs/Web/CSS/timing-function
http://geeklu.com/2012/09/animation-in-ios/

這次先到這里吧,能寫的都盡量寫到,有的東西一想就是一兩天,真的不想敷衍沒意思,每天可能只有一點點時間寫這個。
最后編輯于
©著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
65人點贊
楊千嬅染了紅頭發然而天父并未體恤好人 到我睜開眼 無明燈指引<br>
總資產3共写了1.6W字获得503个赞共652个粉丝
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,786評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,656評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,697評論 0 379
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,098評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,855評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,254評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,322評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,473評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,014評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,833評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,016評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,568評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,273評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,680評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,946評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,730評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,006評論 2 374

推薦閱讀更多精彩內容