美文网首页
CAEmitterLayer(粒子效果)

CAEmitterLayer(粒子效果)

作者: 莪的世界木有如果 | 来源:发表于2018-08-22 17:03 被阅读25次

CAEmitterLayer看上去像是许多CAEmitterCell的容器,这些CAEmitierCell定义了一个例子效果。你将会为不同的例子效果定义一个或多个CAEmitterCell作为模版,同时CAEmitterLayer负责基于这些模版实例化一个粒子流。一个CAEmitterCell类似于一个CALayer:它有一个contents属性可以定义为一个CGImage,另外还有一些可设置属性控制着表现和行为。我们不会对这些属性逐一进行详细的描述,你们可以在CAEmitterCell类的头文件中找到。

GitHub项目地址CAAnimation_CAEmitterLayer工程里面的viewController

下面是对CAEmitterLayer和CAEmitterCell的一些属性说明:

1、CAEmitterLayer

//装着CAEmitterCell对象的数组,被用于把粒子投放到layer上
@property(nullable, copy) NSArray<CAEmitterCell *> *emitterCells;
//粒子产生系数,默认1.0
@property float birthRate;
//粒子生命周期
@property float lifetime;
//发射位置
@property CGPoint emitterPosition;
//发射源的z坐标位置
@property CGFloat emitterZPosition;
//发射源的大小
@property CGSize emitterSize;
//决定粒子形状的深度联系:emitter shape
@property CGFloat emitterDepth;

//发射源的形状
@property(copy) NSString *emitterShape;
- 取值
NSString * const kCAEmitterLayerPoint;
NSString * const kCAEmitterLayerLine;
NSString * const kCAEmitterLayerRectangle;
NSString * const kCAEmitterLayerCuboid;
NSString * const kCAEmitterLayerCircle;
NSString * const kCAEmitterLayerSphere;

//发射模式
@property(copy) NSString *emitterMode;
- 取值
NSString * const kCAEmitterLayerPoints;
NSString * const kCAEmitterLayerOutline;
NSString * const kCAEmitterLayerSurface;
NSString * const kCAEmitterLayerVolume;

//渲染模式
@property(copy) NSString *renderMode;
- 取值
NSString * const kCAEmitterLayerUnordered;
NSString * const kCAEmitterLayerOldestFirst;
NSString * const kCAEmitterLayerOldestLast;
NSString * const kCAEmitterLayerBackToFront;
NSString * const kCAEmitterLayerAdditive;

//粒子是否平展在层上
@property BOOL preservesDepth;
//粒子速度
@property float velocity;
//粒子的缩放比例
@property float scale;
//自旋转速度
@property float spin;
//用于初始化随机数产生的种子
@property unsigned int seed;

2、CAEmitterCell的属性

//初始化方法
+ (instancetype)emitterCell;
//根据健 获 得 值
+ (nullable id)defaultValueForKey:(NSString *)key;
//是否 归 档莫 键值
- (BOOL)shouldArchiveValueForKey:(NSString *)key;
//粒子的名字
@property(nullable, copy) NSString *name;
//粒子是否被渲染
@property(getter=isEnabled) BOOL enabled;
//粒子参数的速度乘数因子
@property float birthRate;
//生命周期
@property float lifetime;
//生命周期范围
@property float lifetimeRange;
//发射的 z 轴方向的角度 
@property CGFloat emissionLatitude;
//x-y 平面的 发 射方向
@property CGFloat emissionLongitude;
//周 围发射角度
@property CGFloat emissionRange;
//速度
@property CGFloat velocity;
//速度范围
@property CGFloat velocityRange;
//粒子 x 方向的加速度分量
@property CGFloat xAcceleration;
//粒子 y 方向的加速度分量
@property CGFloat yAcceleration;
//粒子 z 方向的加速度分量
@property CGFloat zAcceleration;
//缩放比例
@property CGFloat scale;
//缩放比例范围
@property CGFloat scaleRange;
//缩放比例速度
@property CGFloat scaleSpeed;
//子旋转角度
@property CGFloat spin;
//子旋转角度范围
@property CGFloat spinRange;
//粒子的颜色
@property(nullable) CGColorRef color;
//一个粒子的 颜 色 red   能改 变 的范 围
@property float redRange;
//一个粒子的 颜 色 green   能改 变 的范 围
@property float greenRange;
//一个粒子的 颜 色 blue   能改 变 的范 围
@property float blueRange;
//一个粒子的 颜 色 alpha 能改 变 的范 围
@property float alphaRange;
//粒子 red 在生命周期内的改变速度
@property float redSpeed;
//粒子 green 在生命周期内的改变速度
@property float greenSpeed;
//粒子 blue 在生命周期内的改变速度
@property float blueSpeed;
//粒子透明度在生命周期内的改变速度
@property float alphaSpeed;
//是个 CGImageRef 的对象 , 既粒子要展现的图片
@property(nullable, strong) id contents;
//应该画在 contents 里的子 rectangle
@property CGRect contentsRect;
//定义了寄宿图的像素尺寸和视图大小的比例,默认情况下它是一个值为1.0的浮点数
@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;

1、下雨效果

rain.gif

实现代码:

/**
     * 心形雨
     */
    private func setUpRainEmitterLayer(){
        rainEmitterLayer = CAEmitterLayer()
        //渲染模式
        rainEmitterLayer.renderMode = kCAEmitterLayerOldestFirst;
        //发射模式,为线性边缘
        rainEmitterLayer.emitterMode = kCAEmitterLayerOutline;
        //发射源的形状,线性的(就是一条线上都可以发射粒子)
        rainEmitterLayer.emitterShape = kCAEmitterLayerLine;
        //设置发射源的位置
        rainEmitterLayer.emitterPosition = CGPoint(x: view.bounds.midX, y: 0)
        //设置发射源的范围
        rainEmitterLayer.emitterSize = CGSize(width: view.bounds.width, height: 0)
        //设置动画默认停止
        rainEmitterLayer.birthRate = 0
        
        //初始化CAEmitterCell
        let cell = CAEmitterCell()
        //设置cell的内容
        cell.contents = #imageLiteral(resourceName: "Heart_red").cgImage
        //设置scale的系数
        cell.scale = 0.1
        //设置周期时间(每个粒子在屏幕中持续的时间)
        cell.lifetime = 5
        //设置粒子数
        cell.birthRate = 1000
        //设置粒子加速度
        cell.velocity = 500
        //设置发射方向(x-y屏面)
        cell.emissionLongitude = CGFloat.pi
        //设置粒子cell
        rainEmitterLayer.emitterCells = [cell]
        
        self.view.layer.addSublayer(rainEmitterLayer)
    }

rain按钮点击事件:

@IBAction func rainButtonClick(_ sender: UIButton) {
        sender.isUserInteractionEnabled = false
        //设置动画
        let rainAnimation = CABasicAnimation(keyPath: "birthRate")
        rainAnimation.duration = 3.0
        if rainEmitterLayer.birthRate == 0 {
            rainAnimation.fromValue = 0
            rainAnimation.toValue = 1
            rainEmitterLayer.birthRate = 1
        }else{
            rainAnimation.fromValue = 1
            rainAnimation.toValue = 0
            rainEmitterLayer.birthRate = 0
        }
        rainEmitterLayer.add(rainAnimation, forKey: "birthRate")
        DispatchQueue.main.asyncAfter(deadline: .now() + rainAnimation.duration) {
            [weak self] in
            /*
             guard语句和if语句有点类似,都是根据其关键字之后的表达式的布尔值决定下一步执行什么。但与if语句不同的是,guard语句只会有一个代码块,不像if语句可以if else多个代码块。
             
             那么guard语句的作用到底是什么呢?顾名思义,就是守护。guard语句判断其后的表达式布尔值为false时,才会执行之后代码块里的代码,如果为true,则跳过整个guard语句
             */
            guard self != nil else{return}
            sender.isUserInteractionEnabled = true
        }
        
    }

2、上升心形动画

上升心形动画.gif

代码实现:

/**
     * 曲线上升心形动画效果
     */
    private func setUpRightHeartLayer(){
        //设置rightHeartLayer
        rightHeartLayer = CAEmitterLayer()
        //设置发射源的形状
        rightHeartLayer.emitterMode = kCAEmitterLayerPoint
        //设置发射源的发射模式
//        rightHeartLayer.emitterShape = kCAEmitterLayerVolume
        //设置渲染模式
        rightHeartLayer.renderMode = kCAEmitterLayerOldestFirst
        //设置发射源的位置
        rightHeartLayer.emitterPosition = rightHeartButton.center
        //设置birthRate
        rightHeartLayer.birthRate = 0
        
        //初始化CAEmitterLayer
        let cell = CAEmitterCell()
        //设置cell的contents
        cell.contents = #imageLiteral(resourceName: "Heart_red").cgImage
        //设置粒子数
        cell.birthRate = 5
        //设置周期时间
        cell.lifetime = 1
        //设置cell的scale系数
        cell.scale = 0.5
        //设置scale的speed系数
//        cell.scaleSpeed = -0.1
        //设置alpha的speed系数
        cell.alphaSpeed = -1
        //设置粒子速度
        cell.velocity = 50
        //设置粒子发射角度
        cell.emissionLongitude = -CGFloat.pi/2
        cell.emissionRange = CGFloat.pi/4
        
        rightHeartLayer.emitterCells = [cell]
        self.view.layer.addSublayer(rightHeartLayer)
    }

右侧心形按钮的点击方法

@IBAction func rightHeartButtonClick(_ sender: UIButton) {
        sender.isUserInteractionEnabled = false
        //设置时间参数
        rightHeartLayer.beginTime = CACurrentMediaTime()
        /*所有的cells是在一定时间里面生成的,并不是同一时刻生成的,所以如果设置的延迟时间过少的话,cells有可能没有或者只有一两个,这里要注意*/
        rightHeartLayer.birthRate = 1
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {[weak self] in
            guard let strongSelf = self else {return}
            strongSelf.rightHeartLayer.birthRate = 0
        }
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.6) {[weak self] in
            guard self != nil else {return}
            sender.isUserInteractionEnabled = true
        }
    }

3、粒子放射效果

粒子放射效果.gif

代码实现:

/**
     * 粒子放射效果
     */
    private func setUpCenterHeartLayer(){
        //设置centerHeartLayer
        centerHeartLayer = CAEmitterLayer()
        //设置发射源粒的形状
        centerHeartLayer.emitterShape = kCAEmitterLayerCircle
        //设置发射粒子的模式
        centerHeartLayer.emitterMode = kCAEmitterLayerOutline
        //设置渲染效果
        centerHeartLayer.renderMode = kCAEmitterLayerOldestFirst
        //设置发射源的位置
        centerHeartLayer.emitterPosition = centerHeartButton.center
        //设置发射源的大小
        centerHeartLayer.emitterSize = centerHeartButton.bounds.size
        centerHeartLayer.birthRate = 0
        
        //设置CAEmitterCell
        let cell = CAEmitterCell()
        //设置cell的内容
        cell.contents = #imageLiteral(resourceName: "Heart_red").cgImage
        //设置周期时间
        cell.lifetime = 1
        //设置scale大小
        cell.scale = 0.05
        //设置粒子数
        cell.birthRate = 2000
        //设置scale的speed
        cell.scaleSpeed = -0.02
        //设置alpha的speed
        cell.alphaSpeed = -1
        //设置粒子的速度
        cell.velocity = 30
        
        centerHeartLayer.emitterCells = [cell]
        self.view.layer.addSublayer(centerHeartLayer)
        
    }

中间心形按钮点击事件:

 @IBAction func centerHeartButtonClick(_ sender: UIButton) {
        sender.isUserInteractionEnabled = false
        //设置时间参数
        centerHeartLayer.beginTime = CACurrentMediaTime()//在0.2秒后设置birthrate=0,这个0.2秒的时间内,cell只生成一部分,并没有2000个全部完成。
        centerHeartLayer.birthRate = 1
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {[weak self] in
            guard let strongSelf = self else {return}
           strongSelf.centerHeartLayer.birthRate = 0
        }
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {[weak self] in
            guard self != nil else {return}
            sender.isUserInteractionEnabled = true
        }
    }

4、烟花效果

烟花效果.gif

代码实现:

private func setUpGratilyEmitterLayer(layer:CAEmitterLayer!,point:CGPoint!,angle:CGFloat!,angleRange:CGFloat!){
        //设置发射源的形状
        layer.emitterMode = kCAEmitterLayerPoint
        //设置发射源的发射模式
        layer.emitterMode = kCAEmitterLayerVolume
        //设置渲染模式
        layer.renderMode = kCAEmitterLayerOldestFirst
        //设置发射源的位置
        layer.emitterPosition = point
        //设置birthRate
        layer.birthRate = 0
        
        //初始化红❤️EmitterCell
        let redCell = CAEmitterCell()
        //设置cell的contents
        redCell.contents = #imageLiteral(resourceName: "Heart_red").cgImage
        //设置cell的scale
        redCell.scale = 0.5
        //设置alpha
        redCell.alphaSpeed = -0.1
        //设置周期时间
        redCell.lifetime = 20
        //设置粒子速度
        redCell.velocity = 100
        //设置粒子个数
        redCell.birthRate = 10
        //设置Y轴方向的分支速度
        redCell.yAcceleration = 20
        //设置发射角度
        redCell.emissionLongitude = angle
        /*设置发射角度的浮动值(这个浮动值是基于emissionLongitude的角度位置的,比如
emissionLongitude是CGFloat.pi/4,emissionRange为CGFloat.pi/4,则发射点发射
角度浮动范围就在CGFloat.pi/4-CGFloat.pi/8到 CGFloat.pi/4 + CGFloat.pi/8)*/
        redCell.emissionRange = angleRange
        //粒子自旋角度
        redCell.spin = 0
        //粒子自旋角度浮动值
        redCell.spinRange = CGFloat.pi * 2
        
        //初始化蓝❤️EmitterCell
        let blueCell = CAEmitterCell()
        //设置cell的contents
        blueCell.contents = #imageLiteral(resourceName: "Heart_blue").cgImage
        //设置scale
        blueCell.scale = 0.5
        //设置周期时间
        blueCell.lifetime = 20
        //设置粒子数
        blueCell.birthRate = 5
        //设置粒子速度
        blueCell.velocity = 130
        //设置alpha
        blueCell.alphaSpeed = -0.1
        //设置Y轴的分支速度
        blueCell.yAcceleration = 20
        //设置发射角度
        blueCell.emissionLongitude = angle
        //设置发射角度浮动值
        blueCell.emissionRange = angleRange
        //设置自旋角度
        blueCell.spin = 0
        //设置自旋角度浮动值
        blueCell.spinRange = CGFloat.pi * 2
        
        layer.emitterCells = [redCell,blueCell]
        
        view.layer.addSublayer(layer)
    }

gravity按钮点击事件:

@IBAction func gravityButtonClick(_ sender: UIButton) {
        sender.isUserInteractionEnabled = false
        leftGravityLayer.beginTime = CACurrentMediaTime()
        rightGravityLayer.beginTime = CACurrentMediaTime()
        leftGravityLayer.birthRate = 1
        rightGravityLayer.birthRate = 1
        DispatchQueue.main.asyncAfter(deadline: .now() + 10) {[weak self] in
            guard let strongSelf = self else {return}
            strongSelf.leftGravityLayer.birthRate = 0
            strongSelf.rightGravityLayer.birthRate = 0
        }
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {[weak self] in
            guard self != nil else {return}
            sender.isUserInteractionEnabled = true
        }
    }  
}

一些自己研究过程中遇到比较难解的问题在代码中已经注释了。

相关文章

网友评论

      本文标题:CAEmitterLayer(粒子效果)

      本文链接:https://www.haomeiwen.com/subject/eifjiftx.html