CAAnimation
是一個(gè)動(dòng)畫(huà)抽象類(lèi),但是不要直接使用CAAnimation
類(lèi),而是使用它的子類(lèi),如上圖所示展示的就是它的家族成員子類(lèi)們,CAAnimation
遵守CAMediaTiming
和CAAction
協(xié)議,它是直接作用在CALayer
上的,并非UIView
上,動(dòng)畫(huà)執(zhí)行過(guò)程不在主線程上進(jìn)行,所以不會(huì)阻塞主線程火脉。先來(lái)看看CAnimation
都定義了哪些屬性,方法毯辅。
+ animation
初始化一個(gè)CAAnimation 對(duì)象
+ (instancetype)animation;---
**`CAAnimation`**是一個(gè)動(dòng)畫(huà)抽象類(lèi),但是不要直接使用`CAAnimation`類(lèi),而是使用它的子類(lèi),如上圖所示展示的就是它的家族成員子類(lèi)們,`CAAnimation`遵守`CAMediaTiming`和`CAAction`協(xié)議,它是直接作用在`CALayer`上的,并非`UIView`上,動(dòng)畫(huà)執(zhí)行過(guò)程不在主線程上進(jìn)行,所以不會(huì)阻塞主線程恒削。先來(lái)看看`CAnimation`都定義了哪些屬性,方法。
**`+ animation`**初始化一個(gè)CAAnimation 對(duì)象
- (instancetype)animation;
`+defaultValueForKey:` `- shouldArchiveValueForKey:`后者通過(guò)傳入一個(gè)關(guān)鍵字對(duì)動(dòng)畫(huà)對(duì)象 進(jìn)行序列化本地存儲(chǔ),并且返回是否成功镀钓。然后使用相同的關(guān)鍵字調(diào)用前者來(lái)獲取這個(gè)持久化的對(duì)象。
- (nullable id)defaultValueForKey:(NSString *)key;
- (BOOL)shouldArchiveValueForKey:(NSString *)key;
**Timing Function (CAMediaTimingFunction)**
Timing Function會(huì)被用于變化起點(diǎn)到終點(diǎn)之間的插值計(jì)算,形象的說(shuō)是Timing Function決定了動(dòng)畫(huà)運(yùn)行的節(jié)奏(Pacing),比如均勻變化(相同時(shí)間變化量相同),先快后慢,先慢后快還是不停變化速度审孽。
Timing Function對(duì)應(yīng)的類(lèi)是`CAMediaTimingFunction`。
@property(nullable, strong) CAMediaTimingFunction *timingFunction;
`CAMediaTimingFunction`提供了兩種獲得時(shí)間函數(shù)的方法,一種是使用系統(tǒng)提供的五種時(shí)間函數(shù),一種是自定義的時(shí)間函數(shù)废岂。先來(lái)看第一種:
- (id)functionWithName:(NSString *)name;
設(shè)置不同的`name`就能調(diào)用不同的系統(tǒng)提供的時(shí)間函數(shù),`name`的可選值有:
/** Timing function names. **/
//線性 :勻速(速度不變,加速度為0)
A_EXTERN NSString * const kCAMediaTimingFunctionLinear
__OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);
//漸進(jìn):動(dòng)畫(huà)緩慢進(jìn)入,然后加速到達(dá)目的地
CA_EXTERN NSString * const kCAMediaTimingFunctionEaseIn
__OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);
//漸出:動(dòng)畫(huà)全速進(jìn)入,然后再減速到達(dá)目的地
CA_EXTERN NSString * const kCAMediaTimingFunctionEaseOut
__OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);
//漸進(jìn)漸出:動(dòng)畫(huà)緩慢進(jìn)入,中間加速,然后減速到達(dá)目的地,這個(gè)是默認(rèn)的動(dòng)畫(huà)行為
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);
我們來(lái)看下系統(tǒng)提供的這五種時(shí)間函數(shù)的具體效果:(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)
* 默認(rèn) (kCAMediaTimingFunctionDefault
):
![](http://upload-images.jianshu.io/upload_images/276769-8de7f89e5091e4cd.gif?imageMogr2/auto-orient/strip)
**cubic-bezier() 時(shí)間函數(shù)**
你也可以使用`+ functionWithControlPoints::::`或者`- initWithControlPoints::::`自定義自己的時(shí)間函數(shù)(cubic-bezier() )。
cubic-bezier() 定義了一條立方貝塞爾曲線(cubic Bézier curves)狱意。這些曲線是連續(xù)的,一般用于動(dòng)畫(huà)的平滑變換,也被成為緩動(dòng)函數(shù)(easing functions)湖苞。
系統(tǒng)提供的四種時(shí)間函數(shù)的曲線圖(不包含默認(rèn)的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,真實(shí)動(dòng)畫(huà)的持續(xù)時(shí)間,值的變化范圍都是不定的,所以要按照比例進(jìn)行換算,這個(gè)比較好理解就不細(xì)講了。
縱軸x(t)代表的是值改變的量,橫軸t代表時(shí)間详囤。
值的改變量的意思就是當(dāng)前時(shí)間的值與初始值的差值(是差值并不是當(dāng)前時(shí)刻的值,因?yàn)樵c(diǎn)是(0,0)),這個(gè)值的類(lèi)型有很多下面會(huì)細(xì)講财骨。
**形象一點(diǎn)的話你可以把x(t)看作當(dāng)前位置距離起點(diǎn)的距離,那么速度 v = dx/dt , 加速度 a = dv/dt(求導(dǎo))。**所以通過(guò)這個(gè)曲線就可以表現(xiàn)出:動(dòng)畫(huà)在不同時(shí)刻的差值(與初始值相比較),值變化的速度,以及加速度藏姐。當(dāng)v<0的時(shí)候就表示著x(t)的值是負(fù)增長(zhǎng),就好像汽車(chē)一直行駛的時(shí)候,開(kāi)始倒車(chē)了隆箩。
這個(gè)圖理解了,再來(lái)看看 Bézier curve (貝塞爾曲線)
貝塞爾曲線于1962年,由法國(guó)工程師Pierre Bézier所廣泛發(fā)表羔杨,他運(yùn)用貝塞爾曲線來(lái)為汽車(chē)的主體進(jìn)行設(shè)計(jì)捌臊。貝塞爾曲線最初由Paul de Casteljau于1959年運(yùn)用de Casteljau算法開(kāi)發(fā),以穩(wěn)定數(shù)值的方法求出貝塞爾曲線兜材。
`+ functionWithControlPoints::::`或者`- initWithControlPoints::::`構(gòu)建的貝塞爾曲線是一條三次曲線,一條三次貝塞爾曲線如下圖所示需要四個(gè)點(diǎn)來(lái)定義,P0理澎、P1、P2 和 P3曙寡。P0和P3是起點(diǎn)和終點(diǎn),這兩個(gè)點(diǎn)被作為比例固定在坐標(biāo)系上,橫軸為時(shí)間比例,縱軸為完成狀態(tài)糠爬。P0(0, 0)表示初始時(shí)間和初始狀態(tài),P3是(1,1),表示終止時(shí)間和終止?fàn)顟B(tài)。上述兩個(gè)方法的四個(gè)參數(shù)就是P1 , P2的坐標(biāo)卵皂。
![](http://upload-images.jianshu.io/upload_images/276769-8afe20d291d93a77.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
三次貝塞爾曲線演示動(dòng)畫(huà)秩铆,*t*在[0,1]區(qū)間
![Bézier_3](http://upload-images.jianshu.io/upload_images/276769-75f690bd96dcf78e.gif?imageMogr2/auto-orient/strip)
對(duì)貝塞爾曲線不理解的可以多查查相關(guān)資料,講了這么多,如果你想自己自定義一條貝塞爾曲線并看看實(shí)際效果可以利用http://cubic-bezier.com/ 這個(gè)網(wǎng)站自己動(dòng)手實(shí)驗(yàn)。
![演示](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`下面會(huì)利用`CAAnimation`的派生類(lèi)`CABasicAnimation`進(jìn)行講解 灯变。
@property(getter=isRemovedOnCompletion) BOOL removedOnCompletion;
**`CAAnimationDelegate`**
//當(dāng)動(dòng)畫(huà)開(kāi)始的時(shí)候被調(diào)用
- (void)animationDidStart:(CAAnimation *)anim;
//當(dāng)動(dòng)畫(huà)結(jié)束的時(shí)候被調(diào)用
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;
##CAPropertyAnimation
`CAPropertyAnimation`是`CAAnimation`的子類(lèi),`CAAnimation`定義的屬性,聲明的方法以及遵循的協(xié)議,`CAPropertyAnimation`也都全部適用殴玛。
`CAPropertyAnimation`有兩個(gè)子類(lèi)`CABasicAnimation`和`CAKeyframeAnimation`,先來(lái)看下`CAPropertyAnimation`。
創(chuàng)建一個(gè)新的animation 對(duì)象,并且設(shè)置它的屬性`
keyPath`添祸。
- (instancetype)animationWithKeyPath:(nullable NSString *)path;
@property(nullable, copy) NSString *keyPath;
`keyPath`文檔是這么描述的** The key-path describing the property to be animated.**這個(gè)keyPath其實(shí)對(duì)應(yīng)的是`layer`的屬性,設(shè)置了KeyPath就代表著,這個(gè)keyPath對(duì)應(yīng)的`layer`屬性將會(huì)做動(dòng)畫(huà)滚粟。
關(guān)鍵來(lái)了,`layer`到底是有多少個(gè)屬性是可以用來(lái)做動(dòng)畫(huà)的?你上網(wǎng)查是查不完全的,但是你可以查詢`CALayer `的**API**文檔:
/** Geometry and layer hierarchy properties. **/
/* The bounds of the layer. Defaults to CGRectZero. Animatable. */
@property CGRect bounds;
描述中只要包含`Animatable`的屬性就都可以用來(lái)做動(dòng)畫(huà)。
繼續(xù)進(jìn)行之前,先看一個(gè)layer易混淆的地方:
`layer`是通過(guò)樹(shù)形結(jié)構(gòu)組織起來(lái)的,共有三種樹(shù)形結(jié)構(gòu)
> * model layer tree (模型層樹(shù))
> * presentation tree (表示層樹(shù))
> * render tree (渲染層樹(shù))
當(dāng)改變layer的值的時(shí)候,model layer tree的值會(huì)馬上改變,通過(guò)render tree渲染,presentation tree以動(dòng)畫(huà)的形式展現(xiàn)layer的某個(gè)屬性值的漸變過(guò)程刃泌。
`model layer tree`中的`layer`是我們通常意義說(shuō)的`layer`,當(dāng)我們修改layer的屬性的時(shí)候,就會(huì)立刻修改model layer tree凡壤。
view1.layer.anchorPoint = CGPointMake(0.6, 0.6);
refer tree(渲染層樹(shù))是動(dòng)畫(huà)的真實(shí)執(zhí)行者,但是它是私有的,開(kāi)發(fā)者不能調(diào)用。
@property(getter=isAdditive) BOOL additive;
默認(rèn)是NO,當(dāng)設(shè)置為YES的時(shí)候,使 Core Animation 在更新 presentation layer 之前將動(dòng)畫(huà)的值添加到 model layer 中去耙替。
詳細(xì)了解可以參考這篇文章:http://ronnqvi.st/multiple-animations/ 亚侠。
@property(getter=isCumulative) BOOL cumulative;
`cumulative`一般和`repeatCount`(動(dòng)畫(huà)重復(fù)的次數(shù))結(jié)合使用,當(dāng)`cumulative`被設(shè)為YES時(shí),下次動(dòng)畫(huà)的值會(huì)在上一次動(dòng)畫(huà)值的基礎(chǔ)上進(jìn)行而不是重新回到初始狀態(tài),可以看下面的兩個(gè)例子,`cumulative`分別被設(shè)置為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;
//動(dòng)畫(huà)重復(fù)的次數(shù)
animation.repeatCount = 3;
//動(dòng)畫(huà)的持續(xù)時(shí)間
animation.duration = 1;
animation.cumulative = NO;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
[_icon.layer addAnimation:animation forKey:@"animation"];
##CABasicAnimation
`CABasicAnimation`是`CAPropertyAnimation `的子類(lèi)一般也是使用`animationWithKeyPath:`對(duì)`CABasicAnimation`進(jìn)行實(shí)例化,并指定layer(帶有Animatable表示)的屬性作為關(guān)鍵路徑進(jìn)行注冊(cè)俗扇。
###CAMediaTiming
`CAMediaTiming`是`CAAnimation`,`CALayer`所遵循的協(xié)議,上面沒(méi)提是因?yàn)楦杏X(jué)結(jié)合具體動(dòng)畫(huà)的創(chuàng)建會(huì)更加直觀硝烂。`CAMediaTiming`協(xié)議定義了一段動(dòng)畫(huà)內(nèi)用來(lái)控制逝去時(shí)間的屬性的結(jié)合,可以精準(zhǔn)的控制時(shí)間。
@property CFTimeInterval beginTime;
指定動(dòng)畫(huà)的開(kāi)始時(shí)間,如果動(dòng)畫(huà)需要延遲幾秒開(kāi)始應(yīng)當(dāng)這樣設(shè)置:`animation.beginTime = CACurrentMediaTime() +?`铜幽。
@property CFTimeInterval duration;
動(dòng)畫(huà)`執(zhí)行一次`的時(shí)長(zhǎng),無(wú)限重復(fù)可設(shè)置為`HUGE_VALF`滞谢。
@property float speed;
`speed`動(dòng)畫(huà)執(zhí)行一次的時(shí)間系數(shù),默認(rèn)是1.0,如果設(shè)置了`speed`的數(shù)值,那么動(dòng)畫(huà)執(zhí)行一次的時(shí)間就等于`duration /speed`,設(shè)置為0動(dòng)畫(huà)將不會(huì)執(zhí)行串稀。
@property CFTimeInterval timeOffset;
`timeOffset`和`speed`不一樣,`timeOffset`不是一個(gè)時(shí)間系數(shù),`timeOffset`是對(duì)時(shí)間進(jìn)行偏移,計(jì)算出動(dòng)畫(huà)在那個(gè)時(shí)刻的狀態(tài)然后開(kāi)始執(zhí)行。
** timeOffset = Duration > timeOffset ? timeOffset : (Duration % timeOffset)**
有一點(diǎn)需要注意,不論timeOffset的值為多少,動(dòng)畫(huà)從哪個(gè)狀態(tài)開(kāi)始執(zhí)行,動(dòng)畫(huà)都會(huì)完整執(zhí)行一次(比如timeOffset = 3.0 , Duration = 6.0;那么動(dòng)畫(huà)將會(huì)從時(shí)間等于3秒的狀態(tài)開(kāi)始執(zhí)行,然后回到初始狀態(tài)執(zhí)行 0 ~ 3的動(dòng)畫(huà))狮杨。
![timeOffset = 3.0 , Duration = 6.0](http://upload-images.jianshu.io/upload_images/276769-1520d91e16a584d9.gif?imageMogr2/auto-orient/strip)
//你可以自己測(cè)試一下,設(shè)置一下repeatCount看看效果,也可以設(shè)置一下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;
動(dòng)畫(huà)執(zhí)行(重復(fù))的次數(shù),默認(rèn)是0母截。
@property CFTimeInterval repeatDuration;
可以理解為動(dòng)畫(huà)執(zhí)行的總時(shí)長(zhǎng),`duration`是動(dòng)畫(huà)執(zhí)行一次的時(shí)長(zhǎng),兩者不一樣,默認(rèn)值為0,不要和`repeatCount`共同使用。
@property BOOL autoreverses;
用來(lái)設(shè)置動(dòng)畫(huà)是否有回放的效果,默認(rèn)為NO橄教。
@property(copy) NSString *fillMode;
fillModel:填充模式清寇。有四個(gè)可選值,只有在`removeOnCompletion`設(shè)置為NO的時(shí)候才會(huì)生效。
`removeOnCompletion`:動(dòng)畫(huà)結(jié)束時(shí)是否自動(dòng)移除,如果設(shè)置為YES,那么你要手動(dòng)給layer賦值,否則layer會(huì)自動(dòng)回到動(dòng)畫(huà)開(kāi)始之前的狀態(tài)护蝶。
layer動(dòng)畫(huà)運(yùn)行的過(guò)程:其實(shí)我們給一個(gè)視圖添加 layer動(dòng)畫(huà)時(shí),真正移動(dòng)并不是我們視圖本身,而是presentation layer 的一個(gè)緩存,動(dòng)畫(huà)開(kāi)始的時(shí)候presentation layer開(kāi)始移動(dòng),原始layer隱藏,動(dòng)畫(huà)結(jié)束時(shí),presentation layer從屏幕上移除原始layer顯示,這就解釋了為什么我們的視圖在結(jié)束后又回到了原的狀態(tài),因?yàn)樗緵](méi)動(dòng)過(guò)颗管。
KCAFillModelRemoved 這個(gè)是默認(rèn)值,也就是說(shuō)動(dòng)畫(huà)開(kāi)始前和動(dòng)畫(huà)結(jié)束后,動(dòng)畫(huà)對(duì)layer都沒(méi)有影響,動(dòng)畫(huà)結(jié)束后,layer會(huì)恢復(fù)到之前的狀態(tài) , 動(dòng)畫(huà)將在設(shè)置的beginTime開(kāi)始執(zhí)行(如果沒(méi)有設(shè)置beginTime,則動(dòng)畫(huà)立即執(zhí)行)。
![KCAFillModelRemoved](http://upload-images.jianshu.io/upload_images/276769-73a6454a3db7f103.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
KACFillModelForwards 當(dāng)動(dòng)畫(huà)結(jié)束后,layer會(huì)一直保持著動(dòng)畫(huà)最后的狀態(tài)滓走。
![KACFillModelForwards](http://upload-images.jianshu.io/upload_images/276769-2de1257bf05da36c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
KCAFillModeBackwards 會(huì)立即執(zhí)行動(dòng)畫(huà)的第一幀,不論是否設(shè)置了beginTime屬性垦江。
![KCAFillModeBackwards](http://upload-images.jianshu.io/upload_images/276769-5cc188ba945d85d1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
KCAFillModeBoth 動(dòng)畫(huà)加入開(kāi)始之前,layer便處于動(dòng)畫(huà)初始狀態(tài),動(dòng)畫(huà)結(jié)束后layer保持動(dòng)畫(huà)最后的狀態(tài)。
![KCAFillModeBoth](http://upload-images.jianshu.io/upload_images/276769-599f559edd7f06c2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
以上是`CAMediaTiming `的內(nèi)容,繼續(xù)看`CABasicAnimation `三個(gè)關(guān)于layer值的屬性搅方。
@property(nullable, strong) id fromValue;
@property(nullable, strong) id toValue;
@property(nullable, strong) id byValue;
`fromValue`: KeyPath對(duì)應(yīng)layer屬性 的初始值比吭。
`toValue`: keyPath對(duì)應(yīng)layer屬性 的結(jié)束值。
伴隨動(dòng)畫(huà)的進(jìn)行,在`duration`的持續(xù)時(shí)間內(nèi),keyPath對(duì)應(yīng)的layer屬性值從fromValue漸漸地變?yōu)閠oValue姨涡。
`byValue`表示動(dòng)畫(huà)期間改變的值,以layer的`position.x`舉例,當(dāng)`fromeValue = @100;` `byValue = @100;`就表示著layer的`position.x`初始值為100 ,動(dòng)畫(huà)期間`position.x`改變的值是100,`position.x`從初始值100變?yōu)?00衩藤。
`toValue`和`byValue`不要同時(shí)使用,作用沖突。
`CABasicAnimation`實(shí)踐
##CAKeyframeAnimation
關(guān)鍵幀(keyframe)使我們能夠定義動(dòng)畫(huà)中任意的一個(gè)點(diǎn),然后讓Core Animation填充所謂的中間幀涛漂。
`CAKeyframeAnimation`與`CABasicAnimation`雖然都是`CAPropertyAnimation`的子類(lèi)但是兩者還是有區(qū)別的:
`CABasicAnimation`:只能讓`keyPath`對(duì)應(yīng)的`layer`的某個(gè)可動(dòng)畫(huà)(Animatable)的屬性,從初始值(fromValue)轉(zhuǎn)變到另外一個(gè)數(shù)值(toValue)赏表。
`CAKeyframeAnimation`:可以使用一個(gè)數(shù)組` NSArray *values`,保存這些數(shù)值(數(shù)值的個(gè)數(shù)不定),并使用數(shù)組`NSArray<NSNumber *> *keyTimes`來(lái)保存這些數(shù)值對(duì)應(yīng)的時(shí)間點(diǎn)。
@property(nullable, copy) NSArray *values;
存放放關(guān)鍵幀(keyframe)的數(shù)組,動(dòng)畫(huà)對(duì)象會(huì)在指定的時(shí)間(duration)內(nèi),依次顯示values數(shù)組中的每一個(gè)關(guān)鍵幀匈仗。
@property(nullable, copy) NSArray<NSNumber *> *keyTimes;
存放對(duì)應(yīng)的關(guān)鍵幀指定的對(duì)應(yīng)的時(shí)間點(diǎn),存放的類(lèi)型必須為`NSNumber`類(lèi)型,取值范圍在0到0.1(會(huì)根據(jù)duration進(jìn)行一個(gè)時(shí)間的換算),keyTimes里面每一個(gè)時(shí)間點(diǎn)對(duì)應(yīng)數(shù)組values的每一幀,如果沒(méi)有設(shè)置keyTimes或者`KeyTimes = @[]`,那么各個(gè)關(guān)鍵幀的時(shí)間會(huì)被系統(tǒng)平分瓢剿。
**需要注意的是`keyTimes`數(shù)組里的時(shí)間點(diǎn),必須要后面的值大于前面的值,而且最好不要使用分?jǐn)?shù)設(shè)置像這樣`@(1/7)`這樣`3/7`或者這樣`@(4/9)`因?yàn)槟憧赡軙?huì)得到一個(gè)除不盡的小數(shù),動(dòng)畫(huà)效果會(huì)出現(xiàn)問(wèn)題,不會(huì)按照你的預(yù)期執(zhí)行,這個(gè)已經(jīng)測(cè)試**。
![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`路徑動(dòng)畫(huà)的路徑,但是一般不直接使用它,使用的是`UIBezierPath`,`UIBezierPath`是`CGPathRef`數(shù)據(jù)類(lèi)型的封裝悠轩。
###UIBezierPath
`UIBezierPath`的作用就是創(chuàng)建直線,曲線,矩形圓弧或其它復(fù)雜的曲線 形狀间狂。
`UIBezierPath`的使用場(chǎng)景有三種:
* 1 . 與`CAKeyAnimation`結(jié)合使用,作為動(dòng)畫(huà)的路徑。
* 2 . 在方法`- (void)drawRect:(CGRect)rect`中畫(huà)圖使用火架。
* 3 . 與`CAShapeLayer`配合使用,貝塞爾曲線給`CAShapeLayer`提供路徑,`CAShapeLayer`在提供的路徑中進(jìn)行渲染鉴象。
這里先看看它與`CAKeyAnimation`的結(jié)合使用。
UIBezierPath 相關(guān)類(lèi)方法
- (instancetype)bezierPathWithRect:(CGRect)rect;
繪制一個(gè)矩形的貝塞爾曲線
![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;
繪制一個(gè)矩形的內(nèi)切圓的貝塞爾曲線
![內(nèi)切圓](http://upload-images.jianshu.io/upload_images/276769-72979c42987b8c37.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![內(nèi)切圓.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;
繪制一個(gè)可以設(shè)置圓角的矩形貝塞爾曲線
![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;
畫(huà)一個(gè)有**部分圓角**矩形的貝塞爾曲線
`rect`:矩形的frame
`corners`:一個(gè)枚舉值,用來(lái)指定矩形哪些角被設(shè)置為圓角,五個(gè)可選值,左上角何鸡、右上角纺弊、左下角、右下角骡男、全部淆游。
typedef NS_OPTIONS(NSUInteger, UIRectCorner) {
UIRectCornerTopLeft = 1 << 0,
UIRectCornerTopRight = 1 << 1,
UIRectCornerBottomLeft = 1 << 2,
UIRectCornerBottomRight = 1 << 3,
UIRectCornerAllCorners = ~0UL
};
cornerRadii:(CGSize)cornerRadii
***cornerRadii 是 CGSize 類(lèi)型 , 你看其它的資料大部分都寫(xiě)的是 這是用來(lái)設(shè)置橢圓半徑的,首先這不能叫半徑,要叫也是叫長(zhǎng)軸(2a),短軸(2b),其次你設(shè)置height(高)是根本沒(méi)有效果的,只有width是有效果的,這個(gè)圓角其實(shí)就是半徑 = width 圓的一段圓弧 , 如下圖所示,你也可以自己做個(gè)測(cè)試,也可以自己做個(gè)路徑動(dòng)畫(huà)***
![](http://upload-images.jianshu.io/upload_images/276769-c2b5d8fa8e7daa58.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
** 一句話總結(jié)就是height 是多少無(wú)所謂 , 類(lèi)似與
layer.cornerRadius **
- (instancetype)bezierPathWithArcCenter:(CGPoint)center
radius:(CGFloat)radius
startAngle:(CGFloat)startAngle
endAngle:(CGFloat)endAngle
clockwise:(BOOL)clockwise;
自己定義一些參數(shù)畫(huà)一段圓弧。
center:圓弧的圓心坐標(biāo)
radius:圓弧的半徑
startAngle:圓弧起始的弧度
endAngle:圓弧結(jié)束的弧度
clockwise:圓弧從起始弧度指向結(jié)束弧度的方向,YES是順時(shí)針?lè)较?NO是逆時(shí)針?lè)较?/p>
**需要注意的startAngel endAngel并不是讓你填入類(lèi)似45度 , 30度這種數(shù)值,startAngel endAngel是弧度,圓一周的弧度數(shù)為2πr/r=2π,360°角=2π弧度,所以如果你想使用使用30度 , 90度這種數(shù)值請(qǐng)進(jìn)行換算,代碼里面M_PI等同于π稽犁。**
![](http://upload-images.jianshu.io/upload_images/276769-c70d9b73779a2da4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
上圖是iOS devlop文檔的配圖,看下面我做的圖更好理解一點(diǎn)。
![](http://upload-images.jianshu.io/upload_images/276769-0992faecc25bbbf5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
下面你就可以自己畫(huà)一個(gè)圓弧看一下效果骚亿。
(instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = ColorWithHexValue(0x5EB35F);
}
return self;
}-
(void)drawRect:(CGRect)rect
{
//設(shè)置線的填充色
[ColorWithHexValue(0xE9816D) setStroke];//新建一個(gè)bezier對(duì)象
UIBezierPath bezierPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 100) radius:30 startAngle:0 endAngle:1.5M_PI clockwise:YES];//設(shè)置線寬度
bezierPath.lineWidth = 1;
//開(kāi)始繪制
[bezierPath stroke];
}
圖像如下所示:
![](http://upload-images.jianshu.io/upload_images/276769-bf0a5e12ab3ab325.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
構(gòu)建路徑Path的相關(guān)方法:
- (void)moveToPoint:(CGPoint)point
改變當(dāng)前點(diǎn)的位置到point點(diǎn),你可以看作是設(shè)置一條Path的起點(diǎn)已亥。
- (void)addLineToPoint:(CGPoint)point
從當(dāng)前點(diǎn)增加一條直線到該point,然后改變當(dāng)前點(diǎn)把point作為當(dāng)前點(diǎn) 。
-(void)addArcWithCenter:(CGPoint)center
radius:(CGFloat)radius
startAngle:(CGFloat)startAngle
endAngle:(CGFloat)endAngle
clockwise:(BOOL)clockwise
這個(gè)就跟上面說(shuō)過(guò)的畫(huà)一段圓弧的方法是一樣的来屠。
- (void)addCurveToPoint:(CGPoint) controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2
構(gòu)建一個(gè)三次的貝塞爾曲線虑椎。
- (void)addCurveToPoint:(CGPoint) controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2
構(gòu)建一段二次貝塞爾曲線。
貝塞爾曲線,上面時(shí)間函數(shù)的時(shí)候就已經(jīng)說(shuō)得比較清楚了,還想更深入的了解的話,維基百科寫(xiě)的很清晰,講的也很清楚,有興趣的可以科學(xué)的查一下俱笛。
UIBezierPath 就先到這里結(jié)束了,通常情況的具體三種用法就是一開(kāi)始介紹它的時(shí)候?qū)懙?自己試試吧捆姜。
繼續(xù)回到`@interface CAKeyframeAnimation : CAPropertyAnimation`
`calculationMdel`計(jì)算模式,主要針對(duì)的動(dòng)畫(huà)的沒(méi)一怔的內(nèi)容都是一個(gè)坐標(biāo)點(diǎn)的情況,也就是針對(duì)anchortPoint和position進(jìn)行的動(dòng)畫(huà),當(dāng)在平面坐標(biāo)系中有多個(gè)離散的點(diǎn)的時(shí)候可以是離散的,也可以是直線相連后進(jìn)行插值計(jì)算,還可以使用圓滑的曲線將他們相連后進(jìn)行差著計(jì)算,calculationMdel提供如下幾種模式:
**插值**:在離散數(shù)據(jù)的基礎(chǔ)上補(bǔ)插連續(xù)函數(shù),使得這條連續(xù)曲線通過(guò)全部給定的離散數(shù)據(jù)點(diǎn).插值是離散函數(shù)逼近的重要方法,利用它通過(guò)函數(shù)在有限個(gè)點(diǎn)處的取值狀況,估算出函數(shù)在其他點(diǎn)處的近似值,插值:用來(lái)填充圖像變換時(shí)像素之間的空隙。
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的默認(rèn)值,表示當(dāng)關(guān)鍵幀是坐標(biāo)點(diǎn)的時(shí)候,關(guān)鍵這之間直接直線連接進(jìn)行插值計(jì)算;
**kCAAnimationDiscrete**: 離散的,就是不進(jìn)行插值計(jì)算,所有關(guān)鍵幀直接逐個(gè)進(jìn)行顯示 ;
**kCAAnimationPaced**:使得動(dòng)畫(huà)均勻進(jìn)行,而不是按keyTimes設(shè)置的或者按關(guān)鍵幀平分時(shí)間,此時(shí)keyTimes和timeFunctions無(wú)效;
**kCAAnimationCubic**對(duì)關(guān)鍵幀為坐標(biāo)點(diǎn)的關(guān)鍵幀進(jìn)行圓滑曲線相連后插值計(jì)算,曲線的形狀可以通過(guò)`tensionValues`,`continuityValues`,`biasValues`來(lái)進(jìn)行調(diào)整自定義,使得運(yùn)動(dòng)的軌跡變得圓滑;
**kCAAnimationCubicPaced**:這個(gè)是基于**kCAAnimationCubic**的,它的作用是使動(dòng)畫(huà)運(yùn)動(dòng)變得均勻,就是系統(tǒng)時(shí)間內(nèi)運(yùn)動(dòng)的距離相同,此時(shí)keyTimes以及timingFunctions也是無(wú)效的迎膜。
![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;
這三個(gè)屬性是對(duì)每一幀的細(xì)節(jié)的設(shè)置,分別是張力值,持續(xù)性值,偏移值泥技。
@property(nullable, copy) NSString *rotationMode;
**rotationModel** 有兩個(gè)可選值:
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);
這兩個(gè)都可以讓做動(dòng)畫(huà)的時(shí)候沿著路徑的切線方向,但是也有稍微的不同,看下面的演示就清楚了。
![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**的子類(lèi),也就是說(shuō)**CABasicAnimation**定義的屬性它也都有,看看**CASpringAnimation**自己獨(dú)有的屬性有哪些磕仅。
***需要注意的是CASpringAnimation是iOS9才引入的動(dòng)畫(huà)類(lèi),按目前APP的開(kāi)發(fā),一般很少有 只支持iOS9以上的,一般都會(huì)兼容iOS7,個(gè)人建議使用POP珊豹。***
@property CGFloat mass;
質(zhì)量,影響圖層運(yùn)動(dòng)時(shí)的彈簧慣性,質(zhì)量越大,動(dòng)畫(huà)運(yùn)動(dòng)的幅度越大,彈簧拉伸壓縮的幅度越大,數(shù)值必須大于0,默認(rèn)是1。
@property CGFloat stiffness;
彈簧的勁度系數(shù)(彈性系數(shù)),必須大于0,默認(rèn)為100榕订。它描述的是單位形變量時(shí)所產(chǎn)生彈力的大小,k值大,說(shuō)明形變單位長(zhǎng)度需要的力大,或者說(shuō)彈簧"韌",勁度系數(shù)在數(shù)值上等于彈簧伸長(zhǎng)(或縮短)單位長(zhǎng)度的彈力店茶。
@property CGFloat damping;
阻尼系數(shù),必須大于或者等于0,默認(rèn)為10。
**阻尼**是指任何振動(dòng)系統(tǒng)在振動(dòng)中劫恒,由于外界作用贩幻、摩擦力等)和/或系統(tǒng)本身固有的原因引起的振動(dòng)幅度逐漸下降的特性,以及此一特性的量化表征两嘴。
在實(shí)際振動(dòng)中丛楚,由于摩擦力總是存在的,所以振動(dòng)系統(tǒng)最初所獲得的能量憔辫,在振動(dòng)過(guò)程中因阻力不斷對(duì)系統(tǒng)做負(fù)功鸯檬,使得系統(tǒng)的能量不斷減少,振動(dòng)的強(qiáng)度逐漸減弱螺垢,振幅也就越來(lái)越小喧务,以至于最后的停止振動(dòng),像這樣的因系統(tǒng)的力學(xué)能枉圃,由于摩擦及轉(zhuǎn)化成內(nèi)能逐漸減少功茴,振幅隨時(shí)間而減弱振動(dòng),稱(chēng)為阻尼振動(dòng)孽亲。
![一個(gè)有阻尼的彈簧振子振動(dòng)示意圖](http://upload-images.jianshu.io/upload_images/276769-5d8867f525d87496.gif?imageMogr2/auto-orient/strip)
@property CGFloat initialVelocity;
連接到彈簧的對(duì)象得得初始速度,默認(rèn)值是0,負(fù)值的時(shí)候,速度方向與運(yùn)動(dòng)方向相反坎穿。
@property(readonly) CFTimeInterval settlingDuration;
動(dòng)畫(huà)的結(jié)算時(shí)間,從spring動(dòng)畫(huà)開(kāi)始到停止的一個(gè)估算時(shí)間,根據(jù)當(dāng)前設(shè)置的動(dòng)畫(huà)參數(shù)進(jìn)行估算。
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/
這次先到這里吧,能寫(xiě)的都盡量寫(xiě)到,有的東西一想就是一兩天,真的不想敷衍沒(méi)意思,每天可能只有一點(diǎn)點(diǎn)時(shí)間寫(xiě)這個(gè)。