CAAnimation wiki


CAAnimation是一個(gè)動(dòng)畫(huà)抽象類(lèi),但是不要直接使用CAAnimation類(lèi),而是使用它的子類(lèi),如上圖所示展示的就是它的家族成員子類(lèi)們,CAAnimation遵守CAMediaTimingCAAction協(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è)。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末玲昧,一起剝皮案震驚了整個(gè)濱河市栖茉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌孵延,老刑警劉巖吕漂,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異尘应,居然都是意外死亡惶凝,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)犬钢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)苍鲜,“玉大人,你說(shuō)我怎么就攤上這事玷犹』焯希” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵歹颓,是天一觀的道長(zhǎng)遍坟。 經(jīng)常有香客問(wèn)我,道長(zhǎng)晴股,這世上最難降的妖魔是什么愿伴? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮电湘,結(jié)果婚禮上隔节,老公的妹妹穿的比我還像新娘。我一直安慰自己寂呛,他們只是感情好怎诫,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著贷痪,像睡著了一般幻妓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上劫拢,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天肉津,我揣著相機(jī)與錄音,去河邊找鬼舱沧。 笑死妹沙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的熟吏。 我是一名探鬼主播距糖,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼玄窝,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了悍引?” 一聲冷哼從身側(cè)響起恩脂,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎趣斤,沒(méi)想到半個(gè)月后俩块,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡唬渗,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了奋渔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片镊逝。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖嫉鲸,靈堂內(nèi)的尸體忽然破棺而出撑蒜,到底是詐尸還是另有隱情,我是刑警寧澤玄渗,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布座菠,位于F島的核電站,受9級(jí)特大地震影響藤树,放射性物質(zhì)發(fā)生泄漏浴滴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一岁钓、第九天 我趴在偏房一處隱蔽的房頂上張望升略。 院中可真熱鬧,春花似錦屡限、人聲如沸品嚣。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)翰撑。三九已至,卻和暖如春啊央,著一層夾襖步出監(jiān)牢的瞬間眶诈,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工瓜饥, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留册养,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓压固,卻偏偏與公主長(zhǎng)得像球拦,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容