CAAnimation
是一個動畫抽象類,但是不要直接使用CAAnimation
類,而是使用它的子類,如上圖所示展示的就是它的家族成員子類們,CAAnimation
遵守CAMediaTiming
和CAAction
協議,它是直接作用在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`
):

* Ease out (`kCAMediaTimingFunctionEaseOut`
):

* Ease in ease out (`kCAMediaTimingFunctionEaseInEaseOut`
):

* 默認 (kCAMediaTimingFunctionDefault
):

**cubic-bezier() 時間函數**
你也可以使用`+ functionWithControlPoints::::`或者`- initWithControlPoints::::`自定義自己的時間函數(cubic-bezier() )。
cubic-bezier() 定義了一條立方貝塞爾曲線(cubic Bézier curves)。這些曲線是連續的,一般用于動畫的平滑變換,也被成為緩動函數(easing functions)。
系統提供的四種時間函數的曲線圖(不包含默認的kCAMediaTimingFunctionDefault):

縱軸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的坐標。

三次貝塞爾曲線演示動畫,*t*在[0,1]區間

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

- (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。


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的動畫)。

//你可以自己測試一下,設置一下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,則動畫立即執行)。

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

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

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

以上是`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)`因為你可能會得到一個除不盡的小數,動畫效果會出現問題,不會按照你的預期執行,這個已經測試**。

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;
繪制一個矩形的貝塞爾曲線

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;
繪制一個矩形的內切圓的貝塞爾曲線


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;
繪制一個可以設置圓角的矩形貝塞爾曲線


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 圓的一段圓弧 , 如下圖所示,你也可以自己做個測試,也可以自己做個路徑動畫***

** 一句話總結就是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等同于π。**

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

下面你就可以自己畫一個圓弧看一下效果。
(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];
}
圖像如下所示:

構建路徑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也是無效的。

@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);
這兩個都可以讓做動畫的時候沿著路徑的切線方向,但是也有稍微的不同,看下面的演示就清楚了。


##@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。
**阻尼**是指任何振動系統在振動中,由于外界作用、摩擦力等)和/或系統本身固有的原因引起的振動幅度逐漸下降的特性,以及此一特性的量化表征。
在實際振動中,由于摩擦力總是存在的,所以振動系統最初所獲得的能量,在振動過程中因阻力不斷對系統做負功,使得系統的能量不斷減少,振動的強度逐漸減弱,振幅也就越來越小,以至于最后的停止振動,像這樣的因系統的力學能,由于摩擦及轉化成內能逐漸減少,振幅隨時間而減弱振動,稱為阻尼振動。

@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"];

參考列表:
[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/
這次先到這里吧,能寫的都盡量寫到,有的東西一想就是一兩天,真的不想敷衍沒意思,每天可能只有一點點時間寫這個。