說到粒子效果就要說到核心動畫Core Animation,由於粒子效果所用到的特殊圖層是包含在覈心動畫框架中的。這個特殊圖層就是CAEmitterLayer。git
CAEmitterLayer是CALayer的一個經常使用子類,CALayer的子類有不少,CAEmitterLayer就是其中之一,CAEmitterLayer是用於實現基於Core Animation的高性能粒子引擎系統。github
粒子系統使用到兩個類CAEmitterLayer與CAEmitterCell,CAEmitterLayer是粒子發射源,用來發射粒子的,它所發射的粒子就是CAEmitterCell(固然粒子也能夠發射粒子,也就是CAEmitterCell也能夠發射CAEmitterCell)。能夠認爲CAEmitterLayer是CAEmitterCell的發射源,經過不一樣的參數設置就會不斷的產生不一樣的粒子。咱們首先來快速看一下這兩個類的經常使用屬性:數組
@property(nullable, copy) NSArray<CAEmitterCell *> *emitterCells; // 用來裝粒子種類的數組, 經過給數組賦值,來支持多個cell
@property float birthRate; //粒子產生係數(倍數),默認1.0
@property float lifetime; // 粒子的生命週期係數, 默認1.0
@property CGPoint emitterPosition; // 決定粒子發射位置的中心點
@property CGFloat emitterZPosition; //三維立體中的位置
@property CGSize emitterSize; // 發射源的尺寸大小
@property CGFloat emitterDepth; // 發射源的深度
@property(copy) NSString *emitterShape; // 發射源的形狀,默認是點point,(還有line,rectangle,circle,cuboid,sphere).
@property(copy) NSString *emitterMode; // 發射模式,默認是volume,(還有points,outline,surface).
@property(copy) NSString *renderMode; // 渲染模式,默認是unordered,還有oldestFirst,oldestLast,backToFront,additive
@property BOOL preservesDepth; //是否須要深度(通常使用這個屬性)
@property float velocity; // 粒子基本速度係數, 默認1.0
@property float scale; // 粒子縮放比例係數, 默認1.0
@property float spin; // 粒子自旋轉速度係數, 默認1.0
@property unsigned int seed; // 隨機數發生器
複製代碼
CAEmitterLayer裏面的全部屬性都在這裏了,可是在實際狀況中可能用不到這麼多的屬性,下面來重點介紹一些屬性:bash
emitterShape是一個枚舉類型,提供以下類型可供選擇:框架
- kCAEmitterLayerPoint 點形狀,發射源的形狀就是一個點
- kCAEmitterLayerLine 線形狀,發射源的形狀是一條線
- kCAEmitterLayerRectangle 矩形狀,發射源的形狀是一個矩形
- kCAEmitterLayerCuboid 立體矩形形狀(3D),發射源是一個立體矩形,這裏要生效的話須要設置z方向的數據,若是不設置就同矩形狀.
- kCAEmitterLayerCircle 圓形形狀,發射源是一個圓形
- kCAEmitterLayerSphere 立體圓形(3D),三維的圓形,一樣須要設置z方向數據,不設置則如同二維的圓.
- kCAEmitterLayerPoints 點模式,發射器是以點的形式發射粒子。發射點就是形狀的某個特殊的點,好比shap是一個點的話,那麼這個點就是中心點,若是是圓形,那麼就是圓心。
- kCAEmitterLayerOutline 輪廓模式,從形狀的邊界上發射粒子。
- kCAEmitterLayerSurface 表面模式,從形狀的表面上發射粒子。
- kCAEmitterLayerVolume 是相對於3D形狀的物體內部發射.
咱們用kCAEmitterLayerLine來講明一下。當你的CAEmitterLayer的emitterSize爲CGSize(10, 10)時,你的所選擇的emitterPosition爲CGPoint(10,10)。那麼形狀爲「Line」的CAEmitterLayer就會在以下圖紫色的直線上產生粒子,對於「Line」來講,emitterSize的高度是被忽略的。性能
咱們能夠這樣理解,emitterPosition是所選emitterShape的中心點,例如對於矩形是對角線交點,對於圓形是圓心,對於直線是中點。而emitterSize則決定了矩形的大小,圓形的大小,直線的長度。 若是我把CAEmitterCell的速度屬性所有設爲0,此時沒有速度的粒子就不會動了,再把發射模式emitterMode設爲kCAEmitterLayerOutline,咱們來看一下這幾種發射形狀的效果圖: 如今咱們利用一樣的方法看下emitterMode的樣式是什麼樣的,咱們設置emitterShape發射形狀爲kCAEmitterLayerRectangle):
- kCAEmitterLayerUnordered 粒子是無序出現的
- kCAEmitterLayerOldestFirst 聲明時間長的粒子會被渲染在最上層
- kCAEmitterLayerOldestLast 聲明時間短的粒子會被渲染在最上層
- kCAEmitterLayerBackToFront 粒子的渲染按照Z軸的先後順序進行
- kCAEmitterLayerAdditive 進行粒子混合
emitterCells:用來裝粒子的數組。每一種粒子就是一個CAEmitterCell。測試
@property(nullable, copy) NSString *name; // 粒子名字, 默認爲nil,有了名字才能找到對應的粒子
@property(getter=isEnabled) BOOL enabled;
@property float birthRate; // 粒子的產生率,默認0
@property float lifetime; // 粒子的生命週期,默認0,以秒爲單位。
@property float lifetimeRange; // 粒子的生命週期的範圍,默認0,以秒爲單位。
@property CGFloat emissionLatitude;// 指定緯度,緯度角表明了在x-z軸平面座標系中與x軸與z軸之間的夾角,默認0
@property CGFloat emissionLongitude; // 指定經度,經度角表明了在x-y軸平面座標系中與x軸與y軸之間的夾角,默認0
@property CGFloat emissionRange; //發射角度範圍,默認0
@property CGFloat velocity; // 速度,默認是0
@property CGFloat velocityRange; //速度範圍,默認是0
@property CGFloat xAcceleration; // 在x方向上的重力加速度份量,默認是0
@property CGFloat yAcceleration;// 在y方向上的重力加速度份量,默認是0
@property CGFloat zAcceleration;// 在z方向上的重力加速度份量,默認是0
@property CGFloat scale; // 粒子在生命週期範圍內的縮放比例, 默認是1
@property CGFloat scaleRange; // 縮放比例範圍,默認是0
@property CGFloat scaleSpeed; // 在生命週期內的縮放速度,默認是0,負數縮小,正數放大;
@property CGFloat spin; // 粒子的平均旋轉速度,默認是0
@property CGFloat spinRange; // 自旋轉角度範圍,弧度制,默認是0
@property(nullable) CGColorRef color; // 粒子的顏色,默認白色
@property float redRange; // 粒子的顏色red,green,blue,alpha能改變的範圍,默認0
@property float greenRange;
@property float blueRange;
@property float alphaRange;
@property float redSpeed; // 粒子速度red,green,blue,alpha在生命週期內的改變的速度,默認都是0
@property float greenSpeed;
@property float blueSpeed;
@property float alphaSpeed;
@property(nullable, strong) id contents; // 粒子的內容,設置爲CGImageRef的對象
@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一些經常使用的屬性:動畫
contents:爲CGImageRef的對象。關於contents會聯想到CALayer了,在CALayer中展現靜態的圖片是須要用到這個屬性。在CAEmitterCell上它的意義也是同樣的。可是由於粒子系統能夠給粒子上色,爲了作出好的顏色變換效果,一般提供的圖片爲白色的純色的圖片,ui
name:粒子的名字,當CAEmitterLayer裏面有不少個cell的時候,能夠給每一個cell設置好名字,要修改一些屬性以達到動畫效果的改變等,就能夠經過KVC拿到這個cell的某個屬性。atom
emissionLongitude:表示粒子飛行方向跟水平座標軸(x軸)之間的夾角,默認是0,順時針方向是正向。例如emissionLongtitude爲0 ,則粒子順着x軸飛行。若是想沿着Y軸向下飛行,那麼能夠設置emissionLongtitude = M_PI_2。
emissionLatitude:這個和emissionLongitude的原理是同樣的,只不過是在三維平面上的x-z軸上與x軸的夾角。
emissionRange則決定了粒子的發射角度範圍,一樣是一個弧度值。表示粒子在沿着emissionLongtitude方向所造成的頂角爲2倍emissionRange的扇形範圍內發散。咱們把emisstionLongtitude設置爲M_PI_2,讓粒子向下飛行,而且讓emissionRange爲M_PI_4,就會獲得一個頂角爲2 * PI/4的扇形區域,那麼粒子就會在這個區域內發射
velocity、velocityRange、xAcceleration 、yAcceleration、zAcceleration:前面兩個是粒子的初速度和初速度的範圍,後面是三個分別是在x、y、z軸方向的加速度,當xAcceleration爲正數時,粒子每秒向x軸正方向加速,爲負數時則向負方向即水平向左加速。當yAcceleration爲正數時,粒子向y軸的負方向加速,也是就是向下加速,不然向上加速。
spin,spinRange:是粒子的自轉屬性,表示每秒鐘粒子自轉的弧度數。粒子的自轉是以弧度制來計算的,表示每秒鐘粒子自轉的弧度數。spin爲正數表明粒子是順時針旋轉的,爲負數的話就是逆時針旋轉。若是粒子的生命週期就是10秒,那麼你想讓你的粒子這個生命週期內恰好自轉1周,若spinRange爲0,那麼粒子的spin值就應該爲((π/180)*360 )/10,就獲得了每秒須要轉動的弧度數。
emitterCells:CAEmitterCell的emitterCells跟CAEmitterLayer的同樣,也是一個CAEmitterCell的數組。咱們基本能夠按照操做CAEmitterLayer的emitterCells同樣來設置咱們粒子的emitterCells。
不過在這裏須要注意的是:
colorBallCell.birthRate = 20.f;
colorBallCell.lifetime = 10.f;
colorBallCell.velocity = 40.f;
colorBallCell.velocityRange = 100.f;
colorBallCell.yAcceleration = 15.f;
colorBallCell.emissionLongitude = M_PI_2;
colorBallCell.emissionRange = M_PI_4l;
colorBallCell.scale = 0.2;
CAEmitterCell*subCell=[CAEmitterCell emitterCell];
subCell.lifetime=5;
subCell.birthRate=3;
subCell.contents=(id)[UIImage imageNamed:@"circle_white"].CGImage;
subCell.velocity=60;
subCell.emissionLongitude= - M_PI_2;
subCell.emissionRange=M_PI_4/4;
subCell.beginTime=5;
subCell.scale=0.5;
colorBallCell.emitterCells=@[subCell];
colorBallLayer.emitterCells = @[colorBallCell];
複製代碼
父粒子的發射方向是子粒子的emissionLongtitude爲0時的飛行方向。
上述內容已將CAEmitterLayer和CAEmitterCell的屬性基本介紹完畢,後續會再作補充,下面咱們來看一個具體的案例,把這些屬性所有串聯起來。
1.設置CAEmitterLayer
設置CAEmitterLayer以及它的一些模式,並添加到要顯示的view的圖層上,固然也能夠替換view的圖層。這裏是直接添加到控制器的view的layer上的,這個view的layer是一個calyer類型的,若是想替換掉calyer,能夠自定義view,並在view裏面重寫以下方法便可實現替換
// 替換view的layer
+ (Class)layerClass{return [CAEmitterLayer class];}
複製代碼
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) CAEmitterLayer * colorBallLayer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = [UIColor blackColor];
[self setupEmitter];
}
- (void)setupEmitter{
UILabel * label = [[UILabel alloc] initWithFrame:CGRectMake(0, 100, self.view.bounds.size.width, 50)];
[self.view addSubview:label];
label.textColor = [UIColor whiteColor];
label.text = @"輕點或拖動來改變發射源位置";
label.textAlignment = NSTextAlignmentCenter;
// 1. 設置CAEmitterLayer
CAEmitterLayer * colorBallLayer = [CAEmitterLayer layer];
[self.view.layer addSublayer:colorBallLayer];
self.colorBallLayer = colorBallLayer;
//發射源的尺寸大小
colorBallLayer.emitterSize = CGSizeMake(100, 10);
//發射源的形狀
colorBallLayer.emitterShape = kCAEmitterLayerPoint;
//發射模式
colorBallLayer.emitterMode = kCAEmitterLayerPoints;
//粒子發射形狀的中心點
colorBallLayer.emitterPosition = CGPointMake(self.view.layer.bounds.size.width/2, 20);
// 2. 配置CAEmitterCell
CAEmitterCell * colorBallCell = [CAEmitterCell emitterCell];
//粒子名稱
colorBallCell.name = @"colorBallCell";
//粒子產生率,默認爲0
colorBallCell.birthRate = 20.f;
//粒子生命週期
colorBallCell.lifetime = 10.f;
//粒子速度,默認爲0
colorBallCell.velocity = 40.f;
//粒子速度平均量
colorBallCell.velocityRange = 100.f;
//x,y,z方向上的加速度份量,三者默認都是0
colorBallCell.yAcceleration = 15.f;
//指定緯度,緯度角表明了在x-z軸平面座標系中與x軸之間的夾角,默認0:
colorBallCell.emissionLongitude = M_PI_2; // 向左
//發射角度範圍,默認0,以錐形分佈開的發射角度。角度用弧度制。粒子均勻分佈在這個錐形範圍內;
colorBallCell.emissionRange = M_PI_4; // 圍繞X軸向左90度
// 縮放比例, 默認是1
colorBallCell.scale = 0.2;
// 縮放比例範圍,默認是0
colorBallCell.scaleRange = 0.1;
// 在生命週期內的縮放速度,默認是0
colorBallCell.scaleSpeed = 0.02;
// 粒子的內容,爲CGImageRef的對象
colorBallCell.contents = (id)[[UIImage imageNamed:@"circle_white"] CGImage];
//顏色
colorBallCell.color = [[UIColor colorWithRed:0.5 green:0.f blue:0.5 alpha:1.f] CGColor];
// 粒子顏色red,green,blue,alpha能改變的範圍,默認0
colorBallCell.redRange = 1.f;
colorBallCell.greenRange = 1.f;
colorBallCell.alphaRange = 0.8;
// 粒子顏色red,green,blue,alpha在生命週期內的改變速度,默認都是0
colorBallCell.blueSpeed = 1.f;
colorBallCell.alphaSpeed = -0.1f;
CAEmitterCell*subCell=[CAEmitterCell emitterCell];
subCell.lifetime=5;
subCell.birthRate=3;
subCell.contents=(id)[UIImage imageNamed:@"circle_white"].CGImage;
subCell.velocity=60;
subCell.emissionLongitude= - M_PI_2;
subCell.emissionRange=M_PI_4/4;
subCell.beginTime=5;
subCell.scale=0.5;
colorBallCell.emitterCells=@[subCell];
// 添加
colorBallLayer.emitterCells = @[colorBallCell];
}
複製代碼
到此CAEmitterLayer和CAEmitterCell的屬性配置都已完成,下面咱們讓發射源根據手指的點擊位置進行改變:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
CGPoint point = [self locationFromTouchEvent:event];
[self setBallInPsition:point];
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
CGPoint point = [self locationFromTouchEvent:event];
[self setBallInPsition:point];
}
/**
* 獲取手指所在點
*/
- (CGPoint)locationFromTouchEvent:(UIEvent *)event{
UITouch * touch = [[event allTouches] anyObject];
return [touch locationInView:self.view];
}
複製代碼
下面咱們用一個動畫來實現移動發射源到手指點擊的點上:
- (void)setBallInPsition:(CGPoint)position{
//建立基礎動畫
CABasicAnimation * anim = [CABasicAnimation animationWithKeyPath:@"emitterCells.colorBallCell.scale"];
//fromValue
anim.fromValue = @0.2f;
//toValue
anim.toValue = @0.5f;
//duration
anim.duration = 1.f;
//線性起搏,使動畫在其持續時間內均勻地發生
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
// 用事務包裝隱式動畫
[CATransaction begin];
//設置是否禁止因爲該事務組內的屬性更改而觸發的操做。
[CATransaction setDisableActions:YES];
//爲colorBallLayer 添加動畫
[self.colorBallLayer addAnimation:anim forKey:nil];
//爲colorBallLayer 指定位置添加動畫效果
[self.colorBallLayer setValue:[NSValue valueWithCGPoint:position] forKeyPath:@"emitterPosition"];
//提交動畫
[CATransaction commit];
}
複製代碼
下面咱們來看下實現的效果:
至此,一個簡單的粒子效果的代碼就寫完成了,其實大部份內容都是在配置CAEmitterLayer與CAEmitterCell的屬性,由於這兩個類是蘋果爲開發者封裝好的,能夠直接使用,已經省去了大部分的時間,咱們只須要調用屬性便可。在之後的時間中,或許能夠寫一個OpenGL ES 的GLSL語言來實現粒子效果。
demo地址:github.com/Henry-Jeann…