想必以前QQ空間的點(diǎn)贊效果大家都知道吧,點(diǎn)贊之后按鈕周?chē)鷷?huì)有一圈爆裂的小圓點(diǎn);還有微信的紅包雨表情動(dòng)畫(huà)等,以及煙花,火焰效果。這些看似很炫酷的動(dòng)畫(huà)可能讓我們敬而遠(yuǎn)之,但是其實(shí)iOS封裝的很好,利用簡(jiǎn)單的幾行代碼就能完成很炫酷的動(dòng)畫(huà)效果。由于目前正在玩兒iOS動(dòng)畫(huà)的內(nèi)容,利用iOS的CAEmitterLayer結(jié)合CAEmitterCell能夠達(dá)這些效果。不BB了,先上幾個(gè)效果圖。代碼已傳githubEmitterAnimation。


馬丹 有沒(méi)有辦法一次性放很多個(gè)gif呀。。。。。。。
CAEmitterLayer與CAEmitterCell
CAEmitterLayer是CALayer的一個(gè)常用子類(lèi),CALayer的子類(lèi)有很多,如果能很好的使用它們會(huì)得到一些意想不到的效果。CAEmitterLayer就是其中之一,CAEmitterLayer是用于實(shí)現(xiàn)基于Core Animation的粒子發(fā)生器系統(tǒng)。

所謂粒子,就是很多小顆粒,當(dāng)讓QQ空間的點(diǎn)贊動(dòng)畫(huà)的粒子也可以不用CAEmitterLayer粒子發(fā)生器來(lái)實(shí)現(xiàn),不過(guò)這樣會(huì)麻煩很多。在粒子系統(tǒng)中,CAEmitterLayer是用來(lái)發(fā)射粒子的,他所發(fā)射的粒子就是CAEmitterCell(當(dāng)然粒子也可以發(fā)射粒子,也就是CAEmitterCell也可以發(fā)射CAEmitterCell)。可以認(rèn)為CAEmitterLayer是CAEmitterCell的工廠,通過(guò)不同的設(shè)置就會(huì)不斷的產(chǎn)生想要的粒子。
原理其實(shí)很簡(jiǎn)單,但是動(dòng)畫(huà)就是這樣,需要花時(shí)間去理解屬性,只有很好的用它的屬性,才能達(dá)到很炫酷的效果。查看API會(huì)發(fā)現(xiàn)CAEmitterLayer和CAEmitterCell的屬性都是很多的,并且有很多相同的屬性。在CAEmitterLayer中,一些屬性決定了粒子從什么樣的幾何特性上發(fā)射出來(lái),這個(gè)幾何特性包括了位置,形狀,大小,并且還有一些渲染屬性,用于一些渲染的效果。另外一些屬性CAEmitterLayer和CAEmitterCell都有的,在這里可能會(huì)迷糊,但是API說(shuō)明的很清楚,CAEmitterLayer的這些屬性會(huì)作為CAEmitterCell相同屬性的系數(shù),舉個(gè)??,如果CAEmitterCell的birthRate = 10(每秒產(chǎn)生的粒子數(shù)量),其所屬的CAEmitterLayer的birthRate = 2,那么在其他參數(shù)默認(rèn)的情況下,這個(gè)CAEmitterCell總的每秒產(chǎn)生的粒子數(shù)量是10 * 2 = 20 。也就是每秒會(huì)產(chǎn)生20個(gè)這樣的粒子。
另外,會(huì)發(fā)現(xiàn)CAEmitterCell的很多屬性都帶有一個(gè)Range,比如scaleRange、velocityRange,這些決定粒子自身的一些特性的屬性大多都是以“中間值” + 范圍(Range)的方式表示的。再舉個(gè)??,比如scale = 0.5(縮放值)和scaleRange = 0.2(縮放的范圍),那么表示的實(shí)際CAEmitterCell的縮放就是scale±scaleRange,即0.3~0.7這個(gè)范圍。
紅包雨demo
初步了解了這些之后,我們就可以跟著代碼來(lái)實(shí)現(xiàn)實(shí)現(xiàn)一個(gè)紅包雨的功能。實(shí)現(xiàn)起來(lái)很簡(jiǎn)單,只要設(shè)置好屬性就行了,這些屬性的詳細(xì)含義會(huì)在下面的篇幅仔細(xì)講解,先來(lái)試下一個(gè)小demo。就是最開(kāi)始的第個(gè)效果圖。
實(shí)現(xiàn)步驟
- 設(shè)置
CAEmitterLayer以及它的一些模式,并添加到要顯示的view的圖層上,當(dāng)然也可以替換view的圖層 - 給這個(gè)
CAEmitterLayer配置CAEmitterCell。 - 沒(méi)有第三步了。
- 設(shè)置
CAEmitterLayer以及它的一些模式,并添加到要顯示的view的圖層上,當(dāng)然也可以替換view的圖層。這里是直接添加到控制器的view的layer上的,這個(gè)view的layer是一個(gè)calyer類(lèi)型的,如果想替換掉calyer,可以自定義view,并在view里面重寫(xiě)如下方法即可實(shí)現(xiàn)替換
// 替換view的layer
+ (Class)layerClass{return [CAEmitterLayer class];}
這里并沒(méi)有替換,而是直接添加。當(dāng)然為了后面方便這里把CAEmitterLayer設(shè)置為屬性。詳細(xì)代碼如下,可以看注釋
@interface ViewController ()
@property (nonatomic, strong) CAEmitterLayer * redpacketLayer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self redpacketRain];
}
/**
* 紅包雨
*/
- (void)redpacketRain{
// 1. 設(shè)置CAEmitterLayer
CAEmitterLayer * redpacketLayer = [CAEmitterLayer layer];
[self.view.layer addSublayer:redpacketLayer];
self.redpacketLayer = redpacketLayer;
redpacketLayer.emitterShape = kCAEmitterLayerLine; // 發(fā)射源的形狀 是枚舉類(lèi)型
redpacketLayer.emitterMode = kCAEmitterLayerSurface; // 發(fā)射模式 枚舉類(lèi)型
redpacketLayer.emitterSize = self.view.frame.size; // 發(fā)射源的size 決定了發(fā)射源的大小
redpacketLayer.emitterPosition = CGPointMake(self.view.bounds.size.width * 0.5, -10); // 發(fā)射源的位置
redpacketLayer.birthRate = 0.f; // 每秒產(chǎn)生的粒子數(shù)量的系數(shù)
}
到這里CAEmitterLayer的就設(shè)置完了,需要注意的是發(fā)射源的emitterPosition這個(gè)屬性,是將它放在了頂部,實(shí)現(xiàn)從天上掉下來(lái)的效果。
- 給這個(gè)
CAEmitterLayer配置CAEmitterCell。在redpacketRain方法里面添加如下代碼。值得注意的是,粒子的內(nèi)容contents是一個(gè)CGImageRef類(lèi)型的,用UIImage需要轉(zhuǎn)換為CGImage.
// 2. 配置cell
CAEmitterCell * snowCell = [CAEmitterCell emitterCell];
snowCell.contents = (id)[[UIImage imageNamed:@"red_paceket"] CGImage]; // 粒子的內(nèi)容 是CGImageRef類(lèi)型的
snowCell.birthRate = 10.f; // 每秒產(chǎn)生的粒子數(shù)量
snowCell.lifetime = 20.f; // 粒子的生命周期
snowCell.velocity = 8.f; // 粒子的速度
snowCell.yAcceleration = 1000.f; // 粒子再y方向的加速的
snowCell.scale = 0.5; // 粒子的縮放比例
redpacketLayer.emitterCells = @[snowCell]; // 粒子添加到CAEmitterLayer上
- 再添加一個(gè)按鈕,通過(guò)用KVC設(shè)置
CAEmitterLayer的birthRate來(lái)實(shí)現(xiàn)動(dòng)畫(huà)的開(kāi)始和結(jié)束。
- (IBAction)redpacketClick:(id)sender {
[self.redpacketLayer setValue:@1.f forKeyPath:@"birthRate"];
[self performSelector:@selector(endRedpacketAnimation) withObject:nil afterDelay:2.f];
}
- (void)endRedpacketAnimation{
[self.redpacketLayer setValue:@0.f forKeyPath:@"birthRate"];
}
- run之后點(diǎn)擊按鈕就會(huì)出現(xiàn)上圖的效果了。
CAEmitterLayer與CAEmitterCell的屬性詳解
實(shí)現(xiàn)上面的小demo是不是很簡(jiǎn)單?不到20行代碼而已,用到的屬性和也很少。那么下雨下雪的效果也是這樣實(shí)現(xiàn)的,只不過(guò)是修改屬性值。但是這些效果不夠炫酷,要實(shí)現(xiàn)炫酷的效果得先了解各個(gè)屬性的含義,那么接下來(lái)花大量的篇幅講解CAEmitterLayer與CAEmitterCell的屬性。
CAEmitterLayer常用屬性
@property(nullable, copy) NSArray<CAEmitterCell *> *emitterCells; // 用來(lái)裝粒子的數(shù)組
@property float birthRate; // 粒子產(chǎn)生系數(shù),默認(rèn)1.0
@property float lifetime; // 粒子的生命周期系數(shù), 默認(rèn)1.0
@property CGPoint emitterPosition; // 決定了粒子發(fā)射形狀的中心點(diǎn)
@property CGFloat emitterZPosition;
@property CGSize emitterSize; // 發(fā)射源的尺寸大小
@property CGFloat emitterDepth;
@property(copy) NSString *emitterShape; // 發(fā)射源的形狀
@property(copy) NSString *emitterMode; // 發(fā)射模式
@property(copy) NSString *renderMode; // 渲染模式
@property BOOL preservesDepth;
@property float velocity; // 粒子速度系數(shù), 默認(rèn)1.0
@property float scale; // 粒子的縮放比例系數(shù), 默認(rèn)1.0
@property float spin; // 粒子的自旋轉(zhuǎn)速度系數(shù), 默認(rèn)1.0
@property unsigned int seed; // 隨機(jī)數(shù)發(fā)生器
CAEmitterLayer里面的API里面的所有屬性都已經(jīng)貼出來(lái)并作了說(shuō)明,看看注釋并調(diào)試一下就能理解大部分,接下來(lái)重點(diǎn)說(shuō)說(shuō)一些常用的屬性
1. CAEmitterLayer控制粒子發(fā)射位置和形狀的屬性
CAEmitterLayer發(fā)射的粒子并不是雜亂無(wú)章的,我們可以設(shè)置它發(fā)射粒子時(shí)的位置、幾何圖形等。通過(guò)以下屬性去配置:
-
emitterPosition:決定發(fā)射源的中心點(diǎn),如果比如上面的demo中設(shè)置的為CGPointMake(self.view.bounds.size.width * 0.5, -10),那么x軸方向上就是在view的中心點(diǎn),然后再設(shè)置emitterSize = CGSizeMake(40, 0);的話,那么就是self.view.bounds.size.width * 0.5的左右兩邊各20。API中還提供了一個(gè)emitterZPosition,這個(gè)是用在三維坐標(biāo)中的,筆者暫時(shí)對(duì)三維的沒(méi)有研究。 -
emitterSize: 決定發(fā)射源的大小 -
emitterShape:表示粒子從什么形狀發(fā)射出來(lái),它并不是表示粒子自己的形狀。是一個(gè)枚舉類(lèi)型,提供如下類(lèi)型可供選擇:
kCAEmitterLayerPoint
kCAEmitterLayerLine
kCAEmitterLayerRectangle
kCAEmitterLayerCuboid
kCAEmitterLayerCircle
kCAEmitterLayerSphere
-
kCAEmitterLayerPoint:點(diǎn)形狀,發(fā)射源的形狀就是一個(gè)點(diǎn),位置在上面position設(shè)置的位置 -
kCAEmitterLayerLine:線形狀,發(fā)射源的形狀是一條線,位置在rect的橫向的位于垂直方向中間那條 -
kCAEmitterLayerRectangle:矩形狀,發(fā)射源的形狀是一個(gè)矩形,就是上面生成的那個(gè)矩形rect -
kCAEmitterLayerCuboid:立體矩形形狀(3D),發(fā)射源是一個(gè)立體矩形,這里要生效的話需要設(shè)置z方向的數(shù)據(jù),如果不設(shè)置就同矩形狀 -
kCAEmitterLayerCircle:圓形形狀,發(fā)射源是一個(gè)圓形,形狀為矩形包裹的那個(gè)圓,二維的 -
kCAEmitterLayerSphere:立體圓形(3D),三維的圓形,同樣需要設(shè)置z方向數(shù)據(jù),不設(shè)置則通二維一樣
這些形狀可以在調(diào)試的時(shí)候修改來(lái)看看有什么不同,比如我們?cè)O(shè)置的紅包效果,就是用的kCAEmitterLayerLine,結(jié)合了emitterSize來(lái)是實(shí)現(xiàn)從頂部掉下來(lái)的效果,而emitterSize的height其實(shí)是被忽略的。接下來(lái)我們看如下代碼的效果圖解:
redpacketLayer.emitterPosition = CGPointMake(100, 100);
redpacketLayer.emitterSize = CGSizeMake(20, 0);
redpacketLayer.emitterShape = kCAEmitterLayerLine;

emitterShape的幾種模式其實(shí)很好理解,以emitterPosition的點(diǎn)為中心,然后作一個(gè)對(duì)應(yīng)的形狀,如直線、圓形、矩形,在這個(gè)形狀上產(chǎn)生相應(yīng)的粒子。
-
emitterMode:發(fā)射模式,這個(gè)字段規(guī)定了在特定形狀上發(fā)射的具體形式是什么。它的作用其實(shí)就是進(jìn)一步?jīng)Q定發(fā)射的區(qū)域是在發(fā)射形狀的哪一部份。
kCAEmitterLayerPoints
kCAEmitterLayerOutline
kCAEmitterLayerSurface
kCAEmitterLayerVolume
-
kCAEmitterLayerPoints:點(diǎn)模式,發(fā)射器是以點(diǎn)的形式發(fā)射粒子。發(fā)射點(diǎn)就是形狀的某個(gè)特殊的點(diǎn),比如shap是一個(gè)點(diǎn)的話,那么這個(gè)點(diǎn)就是中心點(diǎn),如果是圓形,那么就是圓心。 -
kCAEmitterLayerOutline:輪廓模式,從形狀的邊界上發(fā)射粒子。 -
kCAEmitterLayerSurface:表面模式,從形狀的表面上發(fā)射粒子。 -
kCAEmitterLayerVolume:是相對(duì)于3D形狀的“球體內(nèi)”或“立方體內(nèi)”發(fā)射,筆者暫時(shí)也不是很了解3D的。
2. CAEmitterLayer決定粒子系數(shù)的屬性
-
birthRate: 粒子產(chǎn)生系數(shù),默認(rèn)1.0;每個(gè)粒子cell的產(chǎn)生率乘以這個(gè)粒子產(chǎn)生系數(shù),得出每一秒產(chǎn)生這個(gè)粒子的個(gè)數(shù)。 即:每秒粒子產(chǎn)生個(gè)數(shù) = layer.birthRate * cell.birthRate ; -
lifetime:粒子的生命周期系數(shù),默認(rèn)1.0。計(jì)算方式同上; -
velocity:粒子速度系數(shù), 默認(rèn)1.0。計(jì)算方式同上; -
scale:粒子的縮放比例系數(shù), 默認(rèn)1.0。計(jì)算方式同上; -
spin:自旋轉(zhuǎn)速度系數(shù), 默認(rèn)1.0。計(jì)算方式同上;
3.CAEmitterLayer決定粒子內(nèi)容的屬性
-
emitterCells:用來(lái)裝粒子的數(shù)組。每種粒子就是一個(gè)CAEmitterCell。在API中可以看到CAEmitterCell是服從CAMediatiming協(xié)議的,可以通過(guò)beginTime來(lái)控制subCell的出現(xiàn)時(shí)機(jī)。
CAEmitterCell常用屬性
@property(nullable, copy) NSString *name; // 粒子名字, 默認(rèn)為nil
@property(getter=isEnabled) BOOL enabled;
@property float birthRate; // 粒子的產(chǎn)生率,默認(rèn)0
@property float lifetime; // 粒子的生命周期,以秒為單位。默認(rèn)0
@property float lifetimeRange; // 粒子的生命周期的范圍,以秒為單位。默認(rèn)0
@property CGFloat emissionLatitude;// 指定緯度,緯度角代表了在x-z軸平面坐標(biāo)系中與x軸之間的夾角,默認(rèn)0:
@property CGFloat emissionLongitude; // 指定經(jīng)度,經(jīng)度角代表了在x-y軸平面坐標(biāo)系中與x軸之間的夾角,默認(rèn)0:
@property CGFloat emissionRange; //發(fā)射角度范圍,默認(rèn)0,以錐形分布開(kāi)的發(fā)射角度。角度用弧度制。粒子均勻分布在這個(gè)錐形范圍內(nèi);
@property CGFloat velocity; // 速度和速度范圍,兩者默認(rèn)0
@property CGFloat velocityRange;
@property CGFloat xAcceleration; // x,y,z方向上的加速度分量,三者默認(rèn)都是0
@property CGFloat yAcceleration;
@property CGFloat zAcceleration;
@property CGFloat scale; // 縮放比例, 默認(rèn)是1
@property CGFloat scaleRange; // 縮放比例范圍,默認(rèn)是0
@property CGFloat scaleSpeed; // 在生命周期內(nèi)的縮放速度,默認(rèn)是0
@property CGFloat spin; // 粒子的平均旋轉(zhuǎn)速度,默認(rèn)是0
@property CGFloat spinRange; // 自旋轉(zhuǎn)角度范圍,弧度制,默認(rèn)是0
@property(nullable) CGColorRef color; // 粒子的顏色,默認(rèn)白色
@property float redRange; // 粒子顏色red,green,blue,alpha能改變的范圍,默認(rèn)0
@property float greenRange;
@property float blueRange;
@property float alphaRange;
@property float redSpeed; // 粒子顏色red,green,blue,alpha在生命周期內(nèi)的改變速度,默認(rèn)都是0
@property float greenSpeed;
@property float blueSpeed;
@property float alphaSpeed;
@property(nullable, strong) id contents; // 粒子的內(nèi)容,為CGImageRef的對(duì)象
@property CGRect contentsRect;
@property CGFloat contentsScale;
@property(copy) NSString *minificationFilter;
@property(copy) NSString *magnificationFilter;
@property float minificationFilterBias;
@property(nullable, copy) NSArray<CAEmitterCell *> *emitterCells; // 粒子里面的粒子
@property(nullable, copy) NSDictionary *style;
CAEmitterCell里面的API里面的大部分屬性作了說(shuō)明,看看注釋并調(diào)試一下就能理解大部分,接下來(lái)重點(diǎn)說(shuō)說(shuō)一些常用的屬性。CAEmitterLayer就是粒子的工廠,但是要實(shí)現(xiàn)效果就需要CAEmitterCell的幫助。
1.CAEmitterCell決定生命狀態(tài)的屬性
-
lifetime、lifetimeRange:粒子在系統(tǒng)上的生命周期,即存活時(shí)間,單位是秒。配合lifetimeRage來(lái)讓粒子生命周期均勻變化,以便可以讓粒子的出現(xiàn)和消失顯得更加離散。 -
birthRate:每秒鐘產(chǎn)生的粒子的數(shù)量,是浮點(diǎn)數(shù)。對(duì)于這個(gè)數(shù)量為浮點(diǎn)數(shù),在測(cè)試的時(shí)候可以靈活使用它。比如你想看粒子的運(yùn)動(dòng)狀態(tài),但是太多了可能會(huì)很迷糊,這時(shí)候你把birthRate = 0.1f,其他參數(shù)不變,就能看到單個(gè)粒子的運(yùn)動(dòng)狀態(tài)。
2.CAEmitterCell決定內(nèi)容的屬性
-
contents:為CGImageRef的對(duì)象。關(guān)于contents會(huì)聯(lián)想到CALayer了,在CALayer中展示靜態(tài)的圖片是需要用到這個(gè)屬性。提供一張圖片,作為粒子系統(tǒng)的粒子。但是因?yàn)榱W酉到y(tǒng)可以給粒子上色,為了做出好的顏色變換效果,通常提供的圖片為純色的圖片,一般為白色。 -
name:粒子的名字。初看沒(méi)什么用,但是當(dāng)CAEmitterLayer里面有很多個(gè)cell的時(shí)候,給每個(gè)cell設(shè)置好名字,要修改一些屬性以達(dá)到動(dòng)畫(huà)效果的改變等,就可以通過(guò)KVC拿到這個(gè)cell的某個(gè)屬性。在后面的幾個(gè)demo中都用用到。
3.CAEmitterCell決定顏色狀態(tài)的屬性
粒子系統(tǒng)之所以能做出炫酷的效果,和它的色彩多樣化有必不可上的關(guān)系,在
CAEmitterCell中提供了較多的顏色控制屬性這部分屬性讓你獲得了控制粒子顏色,顏色變化范圍和速度的能力,你可以憑借它來(lái)完成一些漸變的效果或其它構(gòu)建在它之上的酷炫效果。接下來(lái)就看看這些顏色屬性。
-
color:color是粒子的顏色屬性,這個(gè)顏色屬性的作用是給粒子上色,它的實(shí)現(xiàn)方法很簡(jiǎn)單,就是將contents自身的顏色的RGBA值 *color的RGBA值,就得到最終的粒子的顏色。為了很好的計(jì)算,通常用白色的圖片作為contents,因?yàn)樗腞GB都是255,轉(zhuǎn)換為UIColor中的component就是1,用color乘上它就能得到color設(shè)置的顏色效果。 -
redRange、greenRange、blueRange、alphaRange:這些是對(duì)應(yīng)的color的RGBA的取值范圍,取值范圍為0~1,比如如下設(shè)置中
snowCell.color = [[UIColor colorWithRed:0.1 green:0.2 blue:0.3 alpha:0.5]CGColor];
snowCell.redRange = 0.1;
snowCell.greenRange = 0.1;
snowCell.blueRange = 0.1;
snowCell.alphaRange = 0.1;
對(duì)應(yīng)的RGBA的取值范圍就是:R(00.2)、G(0.10.3)、B(0.20.4)、A(0.40.6)。
-
redSpeed、greenSpeed、blueSpeed、alphaSpeed:這些是對(duì)應(yīng)的是粒子的RGBA的變化速度,取值范圍為0~1。表示每秒鐘的RGBA的變化率。這個(gè)變化率的計(jì)算方式其實(shí)很簡(jiǎn)單,先看下面的幾行代碼:
snowCell.lifetime = 20.f; // 粒子的生命周期
snowCell.color = [[UIColor colorWithRed:0.f green:1.f blue:1.f alpha:1.f]CGColor];
snowCell.redSpeed = 0.2;
這里設(shè)置了粒子顏色的RGBA,以及redSpeed,其他的沒(méi)設(shè)置默認(rèn)為0。粒子的生命周期(lifetime)為20秒,那么這個(gè)粒子從產(chǎn)生到消失的時(shí)間就是20秒。它的Red值為0,redSpeed為0.2,那么在粒子的這個(gè)生命周期內(nèi),粒子的每秒鐘的Rde值就會(huì)增加0.2 * 255,表現(xiàn)在外觀上的狀態(tài)就是粒子顏色在不斷變化,接近白色。最后粒子生命周期結(jié)束的時(shí)候,粒子的color正好是RGBA(1,1,1,1)。當(dāng)然個(gè)變化的速度也可以負(fù)數(shù),計(jì)算方式相同。比如要設(shè)置煙花的效果,那么要讓在爆炸的過(guò)程中顏色變化,就是通過(guò)這樣的設(shè)置達(dá)到的效果。
4.CAEmitterCell決定飛行軌跡的屬性。
CAEmitterLayer雖然控制了粒子的發(fā)射位置和形狀等,但是粒子的飛行同時(shí)也需要自身去決定,比如粒子發(fā)射的角度、發(fā)散的范圍,自轉(zhuǎn)屬性等。那么接下來(lái)就說(shuō)說(shuō)這些屬性。
-
emissionLongitude: 指定經(jīng)度,經(jīng)度角代表了在x-y軸平面坐標(biāo)系中與x軸之間的夾角,默認(rèn)0,弧度制。順時(shí)針?lè)较驗(yàn)檎?。這樣解釋看起來(lái)不好懂,畫(huà)個(gè)圖就明白了。
emissionLatitude
粒子沿著X軸向右飛行,如果emissionLongtitude = 0那么粒子會(huì)沿著X軸向右飛行,如果想沿著Y軸向下飛行,那么可以設(shè)置emissionLongtitude = M_PI_2。 -
emissionLatitude:這個(gè)和emissionLongitude的原理是一樣的,只不過(guò)是在三維平面上的x-z軸上與x軸的夾角。 -
emissionRange:發(fā)射角度范圍,默認(rèn)0,以錐形分布開(kāi)的發(fā)射角度。角度用弧度制。粒子均勻分布在這個(gè)錐形范圍內(nèi)。在二維平面中,若想要以錐形的形式發(fā)射粒子,然粒子的發(fā)散范圍不是一條線,而是一個(gè)錐形區(qū)域(也可以叫做扇形),那么可以通過(guò)emissionRange來(lái)設(shè)置一個(gè)范圍。比如想沿Y軸向下成90度的錐形區(qū)域發(fā)散,那么可以通過(guò)如下代碼設(shè)置:
snowCell.emissionLongitude = M_PI_2;
snowCell.emissionRange = M_PI_4;
實(shí)現(xiàn)的效果如下:

可以看到粒子是沿著Y軸向下成90度的一個(gè)發(fā)散角度。如果想實(shí)現(xiàn)火焰等效果。就可以這樣,把角度調(diào)小一點(diǎn)即可。
-
velocity、velocityRange、xAcceleration、yAcceleration、zAcceleration:前面兩個(gè)是粒子的初速度和初速度的范圍,后面是三個(gè)分別是在x、y、z軸方向的加速度,這個(gè)很好理解,初中就應(yīng)該知道加速度的概念,也就是每秒鐘速度的變化量。在放煙花的效果中,煙花飛上天的過(guò)程中,模擬一個(gè)收重力影響的效果,就可以通過(guò)yAcceleration模擬一個(gè)重力加速度的效果。 -
spin,spinRange:這兩個(gè)屬性很重要,是粒子的自轉(zhuǎn)屬性。在粒子被發(fā)射出去之后,想要實(shí)現(xiàn)自轉(zhuǎn),就需要用到他們。粒子的自轉(zhuǎn)是以弧度制來(lái)計(jì)算的,表示每秒鐘粒子自轉(zhuǎn)的弧度數(shù)。spin為正數(shù)代表粒子是順時(shí)針旋轉(zhuǎn)的,為負(fù)數(shù)的話就是逆時(shí)針選轉(zhuǎn)了。舉個(gè)??:粒子的生命周期就是20秒,那么你想讓你的粒子這個(gè)生命周期內(nèi)剛好自轉(zhuǎn)12周,若spinRange為0,那么粒子的spin值就應(yīng)該為((PI/180)*360 * 2)/20,就得到了每秒需要轉(zhuǎn)動(dòng)的弧度數(shù)。
5.CAEmitterCell子粒子的屬性
-
emitterCells:看到CAEmitterCell的這個(gè)屬性的時(shí)候或許會(huì)有些疑惑,不用驚訝,前面說(shuō)過(guò)CAEmitterLayer可以產(chǎn)生cell,通用cell也可以產(chǎn)生cell。那么這個(gè)屬性就和CAEmitterLayer中的emitterCells一樣,也是一個(gè)數(shù)組。這里有幾個(gè)需要注意的地方:- 若給cell設(shè)置了subCell,若想控制subCell的方向,那么得考慮父cell的方向?qū)傩?,也就?code>emissionLongtitude和
emissionLatitude這兩個(gè)屬性的情況。 - 不管父粒子cell是從什么樣的形狀上發(fā)射出來(lái)的,若要發(fā)射subCell,subCell總是從
kCAEmitterLayerPoint形狀上由父粒子的中心發(fā)射出來(lái)的。
- 若給cell設(shè)置了subCell,若想控制subCell的方向,那么得考慮父cell的方向?qū)傩?,也就?code>emissionLongtitude和
造幾個(gè)小Demo
理解了
CAEmitterLayer與CAEmitterCell的屬性之后,通過(guò)粒子系統(tǒng)實(shí)現(xiàn)一些炫酷的動(dòng)畫(huà)效果就很簡(jiǎn)單了。接下來(lái)對(duì)實(shí)現(xiàn)的幾個(gè)小demo效果作個(gè)思路分享,歡迎提供更好的方法~。
QQ空間點(diǎn)贊動(dòng)畫(huà)
動(dòng)畫(huà)分析
- 點(diǎn)贊的時(shí)候先是把按鈕放大再縮小,這個(gè)可以用核心動(dòng)畫(huà)里面的關(guān)鍵幀動(dòng)畫(huà)
CAKeyframeAnimation實(shí)現(xiàn) - 放大后馬上回有一圈的粒子發(fā)射,這就是粒子系統(tǒng)實(shí)現(xiàn)
- 然后從贊到取消贊沒(méi)有動(dòng)畫(huà)效果
實(shí)現(xiàn)思路
- 自定義按鈕,重寫(xiě)
setHighlighted方法去掉高亮狀態(tài)。提供兩張圖片,用于默認(rèn)狀態(tài)和選中狀態(tài)。 - 配置發(fā)射源
CAEmitterLayer和粒子CAEmitterCell。由于CAEmitterLayer的birthRate默認(rèn)為1,CAEmitterCell的birthRate默認(rèn)為0,那么先不設(shè)置這兩個(gè)屬性。給cell設(shè)置好name,然后自定義一個(gè)動(dòng)畫(huà)開(kāi)始的方法,在這里面通過(guò)KVC設(shè)置CAEmitterCell的birthRate以實(shí)現(xiàn)動(dòng)畫(huà)
/**
* 開(kāi)始動(dòng)畫(huà)
*/
- (void)startAnimation{
// 用KVC設(shè)置顆粒個(gè)數(shù)
[self.explosionLayer setValue:@1000 forKeyPath:@"emitterCells.explosionCell.birthRate"];
// 開(kāi)始動(dòng)畫(huà)
self.explosionLayer.beginTime = CACurrentMediaTime();
// 延遲停止動(dòng)畫(huà)
[self performSelector:@selector(stopAnimation) withObject:nil afterDelay:0.15];
}
/**
* 動(dòng)畫(huà)結(jié)束
*/
- (void)stopAnimation{
// 用KVC設(shè)置顆粒個(gè)數(shù)
[self.explosionLayer setValue:@0 forKeyPath:@"emitterCells.explosionCell.birthRate"];
[self.explosionLayer removeAllAnimations];
}
- 重寫(xiě)按鈕的
setSelected方法,在里面通過(guò)關(guān)鍵幀動(dòng)畫(huà)實(shí)現(xiàn)縮放。
/**
* 選中狀態(tài) 實(shí)現(xiàn)縮放
*/
- (void)setSelected:(BOOL)selected{
[super setSelected:selected];
// 通過(guò)關(guān)鍵幀動(dòng)畫(huà)實(shí)現(xiàn)縮放
CAKeyframeAnimation * animation = [CAKeyframeAnimation animation];
animation.keyPath = @"transform.scale";
if (selected) { // 從沒(méi)有點(diǎn)擊到點(diǎn)擊狀態(tài) 會(huì)有爆炸的動(dòng)畫(huà)效果
animation.values = @[@1.5,@2.0, @0.8, @1.0];
animation.duration = 0.5;
animation.calculationMode = kCAAnimationCubic;
[self.layer addAnimation:animation forKey:nil];
// 讓放大動(dòng)畫(huà)先執(zhí)行完畢 再執(zhí)行爆炸動(dòng)畫(huà)
[self performSelector:@selector(startAnimation) withObject:nil afterDelay:0.25];
}else{ // 從點(diǎn)擊狀態(tài)normal狀態(tài) 無(wú)動(dòng)畫(huà)效果 如果點(diǎn)贊之后馬上取消 那么也立馬停止動(dòng)畫(huà)
[self stopAnimation];
}
}
結(jié)語(yǔ)
還有幾個(gè)動(dòng)畫(huà)的思路就不啰嗦了,實(shí)現(xiàn)起來(lái)都很簡(jiǎn)單,關(guān)鍵是要去理解這些屬性的作用。另外動(dòng)畫(huà)做起來(lái)即使費(fèi)時(shí)間,要自己去理解屬性的作用的話需要花時(shí)間調(diào)試對(duì)比,其實(shí)用其他方式也可以實(shí)現(xiàn)這樣的效果,但是CAEmitterLayer 基于GPU,做這些效果的時(shí)候比較方便。demo已經(jīng)上傳github了,提供了下雨、下雪、紅包雨、五彩小球、愛(ài)心、火焰、煙花等效果,值得一說(shuō)的是,煙花需要三個(gè)cell,一個(gè)提供發(fā)射的shootCell,一個(gè)用于爆炸的explodeCell,還有一個(gè)用于火花的sparkCell,這些都是父子關(guān)系,一個(gè)cell生命周期完了另外一個(gè)再出來(lái)。這些可以查看代碼體會(huì),都有注釋~。EmitterAnimation
