iOS:CAEmitterLayer粒子效果

說到粒子效果就要說到核心動畫Core Animation,由於粒子效果所用到的特殊圖層是包含在覈心動畫框架中的。這個特殊圖層就是CAEmitterLayer。git

CAEmitterLayer是CALayer的一個經常使用子類,CALayer的子類有不少,CAEmitterLayer就是其中之一,CAEmitterLayer是用於實現基於Core Animation的高性能粒子引擎系統。github

CALayer.png
粒子系統使用到兩個類CAEmitterLayer與CAEmitterCell,CAEmitterLayer是粒子發射源,用來發射粒子的,它所發射的粒子就是CAEmitterCell(固然粒子也能夠發射粒子,也就是CAEmitterCell也能夠發射CAEmitterCell)。能夠認爲CAEmitterLayer是CAEmitterCell的發射源,經過不一樣的參數設置就會不斷的產生不一樣的粒子。

咱們首先來快速看一下這兩個類的經常使用屬性:數組

CAEmitterLayer經常使用屬性

@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

1. CAEmitterLayer控制發射源發射位置和形狀的屬性

  • emitterPosition:決定發射源的中心點
  • emitterSize: 決定發射源的大小
  • emitterShape:表示粒子從什麼形狀發射出來,它並非表示粒子本身的形狀。

emitterShape是一個枚舉類型,提供以下類型可供選擇:框架

  • kCAEmitterLayerPoint 點形狀,發射源的形狀就是一個點
  • kCAEmitterLayerLine 線形狀,發射源的形狀是一條線
  • kCAEmitterLayerRectangle 矩形狀,發射源的形狀是一個矩形
  • kCAEmitterLayerCuboid 立體矩形形狀(3D),發射源是一個立體矩形,這裏要生效的話須要設置z方向的數據,若是不設置就同矩形狀.
  • kCAEmitterLayerCircle 圓形形狀,發射源是一個圓形
  • kCAEmitterLayerSphere 立體圓形(3D),三維的圓形,一樣須要設置z方向數據,不設置則如同二維的圓.
  • emitterMode:發射模式,它的做用其實就是進一步決定發射的區域是在發射形狀的哪一部份。
  • kCAEmitterLayerPoints 點模式,發射器是以點的形式發射粒子。發射點就是形狀的某個特殊的點,好比shap是一個點的話,那麼這個點就是中心點,若是是圓形,那麼就是圓心。
  • kCAEmitterLayerOutline 輪廓模式,從形狀的邊界上發射粒子。
  • kCAEmitterLayerSurface 表面模式,從形狀的表面上發射粒子。
  • kCAEmitterLayerVolume 是相對於3D形狀的物體內部發射.

咱們用kCAEmitterLayerLine來講明一下。當你的CAEmitterLayer的emitterSize爲CGSize(10, 10)時,你的所選擇的emitterPosition爲CGPoint(10,10)。那麼形狀爲「Line」的CAEmitterLayer就會在以下圖紫色的直線上產生粒子,對於「Line」來講,emitterSize的高度是被忽略的。性能

示例.png
咱們能夠這樣理解,emitterPosition是所選emitterShape的中心點,例如對於矩形是對角線交點,對於圓形是圓心,對於直線是中點。而emitterSize則決定了矩形的大小,圓形的大小,直線的長度。 若是我把CAEmitterCell的速度屬性所有設爲0,此時沒有速度的粒子就不會動了,再把發射模式emitterMode設爲kCAEmitterLayerOutline,咱們來看一下這幾種發射形狀的效果圖:
kCAEmitterLayerPoint.png
kCAEmitterLayerLine.png
kCAEmitterLayerRectangle.png
kCAEmitterLayerCircle.png
如今咱們利用一樣的方法看下emitterMode的樣式是什麼樣的,咱們設置emitterShape發射形狀爲kCAEmitterLayerRectangle):
kCAEmitterLayerPoints.png
kCAEmitterLayerOutline.png
kCAEmitterLayerSurface.png

  • renderMode : 渲染模式,決定了粒子是以怎麼一種形式進行渲染的。
  • kCAEmitterLayerUnordered 粒子是無序出現的
  • kCAEmitterLayerOldestFirst 聲明時間長的粒子會被渲染在最上層
  • kCAEmitterLayerOldestLast 聲明時間短的粒子會被渲染在最上層
  • kCAEmitterLayerBackToFront 粒子的渲染按照Z軸的先後順序進行
  • kCAEmitterLayerAdditive 進行粒子混合

2. CAEmitterLayer決定發射源粒子係數的屬性

  • birthRate: 種類粒子產生係數,默認1.0;須要結合單個粒子cell的產生係數,每一個粒子cell的產生係數乘以這個粒子產生係數,得出每秒具體產生粒子的個數。 即:每秒粒子產生個數 = layer.birthRate * cell.birthRate ;
  • lifetime:粒子的生命週期係數,默認1.0。
  • velocity:粒子速度係數, 默認1.0。
  • scale:粒子的縮放比例係數, 默認1.0。
  • spin:自旋轉速度係數, 默認1.0。計算方式同上;

3.CAEmitterLayer決定發射源粒子內容的屬性

emitterCells:用來裝粒子的數組。每一種粒子就是一個CAEmitterCell。測試

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一些經常使用的屬性:動畫

1.CAEmitterCell決定粒子生命週期的屬性

  • lifetime、lifetimeRange:粒子在系統上的生命週期,即存活時間,單位是秒。配合lifetimeRage來讓粒子生命週期均勻變化,以即可以讓粒子的出現和消失顯得更加離散。
  • birthRate:粒子產生數量的決定參數,它表示CAEmitterLayer上每秒產生的粒子數量,是浮點數。對於這個數量爲浮點數,在測試的時候能夠靈活使用它。好比你想看粒子的運動狀態,可是太多了可能會很迷糊,這時候你把birthRate 設置成0.1f,把lifetime設置時間較長,就能看到單個粒子的運動狀態。

2.CAEmitterCell決定粒子內容的屬性

  • contents:爲CGImageRef的對象。關於contents會聯想到CALayer了,在CALayer中展現靜態的圖片是須要用到這個屬性。在CAEmitterCell上它的意義也是同樣的。可是由於粒子系統能夠給粒子上色,爲了作出好的顏色變換效果,一般提供的圖片爲白色的純色的圖片,ui

  • name:粒子的名字,當CAEmitterLayer裏面有不少個cell的時候,能夠給每一個cell設置好名字,要修改一些屬性以達到動畫效果的改變等,就能夠經過KVC拿到這個cell的某個屬性。atom

3.CAEmitterCell決定粒子顏色狀態的屬性

  • color:color是粒子的顏色屬性,這個顏色屬性的做用是給粒子上色,color 會結合contents內容的顏色來改變咱們的CAEmitterCell,它的實現方法其實很簡單,就是將contents自身的顏色的RGBA值 * color的RGBA值,就獲得最終的粒子的顏色。咱們想徹底經過color來控制CAEmitterCell的顏色,那麼最好就選用一張顏色值爲(255,255,255),即白色的圖片做爲CAEmitterCell的contents。由於你們都知道在UIColor中,rgb值爲255的component的值其實爲1,由於白色的UIColor每一個component都爲1,CAEmitterCell的color中任意component乘以contents中顏色對應的component都會獲得color上原來的值。
  • redRange、greenRange、blueRange、alphaRange:這些是對應的color的RGBA的取值範圍,默認爲0,取值範圍爲0~1,若是設置粒子的顏色爲[[UIColor colorWithRed:0.3 green:0.5 blue:0.3 alpha:1.0]CGColor];再設置 redRange、greenRange、blueRange、alphaRange所有爲0.1,那麼也就是說在產生粒子時,粒子的顏色RGBA對應的取值範圍是:R(0.1, 0.3),G(0.1, 0.5),B(0.1, 0.3),A(0.1, 1.0).
  • redSpeed、greenSpeed、blueSpeed、alphaSpeed:這些是對應的是粒子的RGBA的變化速度,默認爲0,取值範圍爲0~1。表示每秒鐘的RGBA的變化率,若是設置粒子顏色的RGBA,以及redSpeed,其餘的屬性沒設置默認爲0。粒子的生命週期(lifetime)爲20秒,那麼這個粒子從產生到消失的時間就是20秒。它的Red值爲0,redSpeed爲0.2,那麼在粒子的這個生命週期內,粒子的每秒鐘的Rde值就會增長0.2 * 255,表如今外觀上的狀態就是粒子顏色在不斷變化,接近白色。最後粒子生命週期結束的時候,粒子的color正好是RGBA(1,1,1,1)。固然個變化的速度也能夠負數,計算方式相同。

4.CAEmitterCell決定粒子運行軌跡的屬性。

  • emissionLongitude:表示粒子飛行方向跟水平座標軸(x軸)之間的夾角,默認是0,順時針方向是正向。例如emissionLongtitude爲0 ,則粒子順着x軸飛行。若是想沿着Y軸向下飛行,那麼能夠設置emissionLongtitude = M_PI_2。

    平面運行方向.png

  • emissionLatitude:這個和emissionLongitude的原理是同樣的,只不過是在三維平面上的x-z軸上與x軸的夾角。

  • emissionRange則決定了粒子的發射角度範圍,一樣是一個弧度值。表示粒子在沿着emissionLongtitude方向所造成的頂角爲2倍emissionRange的扇形範圍內發散。咱們把emisstionLongtitude設置爲M_PI_2,讓粒子向下飛行,而且讓emissionRange爲M_PI_4,就會獲得一個頂角爲2 * PI/4的扇形區域,那麼粒子就會在這個區域內發射

    emissionRange角度.png
    效果圖.png

  • velocity、velocityRange、xAcceleration 、yAcceleration、zAcceleration:前面兩個是粒子的初速度和初速度的範圍,後面是三個分別是在x、y、z軸方向的加速度,當xAcceleration爲正數時,粒子每秒向x軸正方向加速,爲負數時則向負方向即水平向左加速。當yAcceleration爲正數時,粒子向y軸的負方向加速,也是就是向下加速,不然向上加速。

  • spin,spinRange:是粒子的自轉屬性,表示每秒鐘粒子自轉的弧度數。粒子的自轉是以弧度制來計算的,表示每秒鐘粒子自轉的弧度數。spin爲正數表明粒子是順時針旋轉的,爲負數的話就是逆時針旋轉。若是粒子的生命週期就是10秒,那麼你想讓你的粒子這個生命週期內恰好自轉1周,若spinRange爲0,那麼粒子的spin值就應該爲((π/180)*360 )/10,就獲得了每秒須要轉動的弧度數。

5.CAEmitterCell子粒子的屬性

emitterCells:CAEmitterCell的emitterCells跟CAEmitterLayer的同樣,也是一個CAEmitterCell的數組。咱們基本能夠按照操做CAEmitterLayer的emitterCells同樣來設置咱們粒子的emitterCells。

不過在這裏須要注意的是:

  1. CAEmitterCell是服從CAMediatiming協議的,咱們經過控制子CAEmitterCell的beginTime來控制子CAEmitterCell的出現時機。當子CAEmitterCell的beginTime爲0時,表示你的粒子從CAEmitterLayer上發射出來後就會當即開始發射子CAEmitterCell,並且子CAEmitterCell的beginTime是不能大於你的粒子的lifetime的。
  2. 不管粒子是從什麼樣的形狀上發射出來的,當它要發射子CAEmitterCell的時候,子CAEmitterCell老是從kCAEmitterLayerPoint形狀上由父粒子的中心發射出來的。
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];
複製代碼

subCell.emissionLongitude.jpeg
父粒子的發射方向是子粒子的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];
}
複製代碼

下面咱們來看下實現的效果:

效果圖.png

至此,一個簡單的粒子效果的代碼就寫完成了,其實大部份內容都是在配置CAEmitterLayer與CAEmitterCell的屬性,由於這兩個類是蘋果爲開發者封裝好的,能夠直接使用,已經省去了大部分的時間,咱們只須要調用屬性便可。在之後的時間中,或許能夠寫一個OpenGL ES 的GLSL語言來實現粒子效果。

demo地址:github.com/Henry-Jeann…

相關文章
相關標籤/搜索