iOS CAEmitterLayer 实现粒子发射动画效果

效果图

代码已上传 GitHub:https://github.com/Silence-GitHub/CoreAnimationDemo

动画效果用 CAEmitterLayer 实现。CAEmitterLayer 显示粒子发射动画,具体的粒子由 CAEmitterCell 封装。代码示例是展示 CAEmitterLayer 如何使用。为了方便,直接在控制器(UIViewController)中设置 CAEmitterLayer。如果在项目中使用,有时在自定义视图(UIView)中加入 CAEmitterLayer 比较合理,例如自定义点赞按钮,可以精简控制器的代码。

下雨动画效果

这里的雨匀速下落,雨的密度逐渐变化。

给控制器添加类型为 CAEmitterLayer 的属性 rainLayer,在 viewDidLoad 方法中对此属性进行初始化

private var rainLayer: CAEmitterLayer!

private func setupRainLayer() {
// 粒子发射图层
rainLayer = CAEmitterLayer()
// 发射器形状为线形,默认发射方向向上
rainLayer.emitterShape = kCAEmitterLayerLine
// 从发射器的轮廓发射粒子
rainLayer.emitterMode = kCAEmitterLayerOutline
// 优先渲染旧的粒子
rainLayer.renderMode = kCAEmitterLayerOldestFirst
// 发射位置
// 对于线形发射器,线的两端点分别为
// (emitterPosition.x - emitterSize.width/2, emitterPosition.y, emitterZPosition)和
// (emitterPosition.x + emitterSize.width/2, emitterPosition.y, emitterZPosition)
rainLayer.emitterPosition = CGPoint(x: view.bounds.midX, y: 0)
// 发射器大小
rainLayer.emitterSize = CGSize(width: view.bounds.width, height: 0)
// 粒子生成速率的倍数,一开始不发射,设置为零
rainLayer.birthRate = 0 // 发射的粒子
let cell = CAEmitterCell()
// 粒子显示的内容,设置CGImage,显示图片
cell.contents = #imageLiteral(resourceName: "Heart_red").cgImage
// 粒子缩放倍数
cell.scale = 0.1
// 粒子寿命,单位是秒
cell.lifetime = 5
// 粒子生成速率,单位是个/秒,实际显示效果要乘以CAEmitterLayer的birthRate
cell.birthRate = 1000
// 粒子速度
cell.velocity = 500
// 粒子发射角度,正值表示顺时针方向
cell.emissionLongitude = CGFloat.pi // 图层要发射1种粒子
rainLayer.emitterCells = [cell]
// 添加粒子发射图层
view.layer.addSublayer(rainLayer)
}

点击按钮开始或停止动画。用 CABasicAnimation 使粒子生成速率的倍数渐变,达到雨逐渐变大或变小的效果

@IBAction func rainButtonClicked(_ sender: UIButton) {
// 连续调用此方法会影响雨变大或变小的连贯性,所以禁止连续点击按钮
sender.isUserInteractionEnabled = false
// 粒子生成速率渐变动画
let birthRateAnimation = CABasicAnimation(keyPath: "birthRate")
birthRateAnimation.duration = 3
if rainLayer.birthRate == 0 {
// 雨变大
birthRateAnimation.fromValue = 0
birthRateAnimation.toValue = 1
rainLayer.birthRate = 1
} else {
// 雨变小
birthRateAnimation.fromValue = 1
birthRateAnimation.toValue = 0
rainLayer.birthRate = 0
}
// 加入动画
rainLayer.add(birthRateAnimation, forKey: "birthRate")
// 动画时长过后恢复按钮可点击状态
DispatchQueue.main.asyncAfter(deadline: .now() + birthRateAnimation.duration) { [weak self] in
guard self != nil else { return }
sender.isUserInteractionEnabled = true
}
}

发射一圈粒子动画效果

给控制器添加类型为 CAEmitterLayer 的属性 centerHeartLayer,在 viewDidLoad 方法中对此属性进行初始化

private var centerHeartLayer: CAEmitterLayer!

private func setupCenterHeartLayer() {
centerHeartLayer = CAEmitterLayer()
// 发射器形状为圆形,默认向四周发射粒子
centerHeartLayer.emitterShape = kCAEmitterLayerCircle
centerHeartLayer.emitterMode = kCAEmitterLayerOutline
centerHeartLayer.renderMode = kCAEmitterLayerOldestFirst
// 发射器位置
// 对于圆形发射器
// 圆心位于(emitterPosition.x, emitterPosition.y, emitterZPosition)
// 半径为emitterSize.width
centerHeartLayer.emitterPosition = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
centerHeartLayer.emitterSize = centerHeartButton.frame.size
centerHeartLayer.birthRate = 0 let cell = CAEmitterCell()
cell.contents = #imageLiteral(resourceName: "Heart_red").cgImage
cell.lifetime = 1
cell.birthRate = 2000
cell.scale = 0.05
// 粒子缩放倍数每秒减小0.02,粒子逐渐缩小
cell.scaleSpeed = -0.02
// 粒子透明度每秒减小1,粒子逐渐变透明
cell.alphaSpeed = -1
cell.velocity = 30 centerHeartLayer.emitterCells = [cell]
view.layer.addSublayer(centerHeartLayer)
}

点击按钮开始动画

@IBAction func centerHeartButtonClicked(_ sender: UIButton) {
sender.isUserInteractionEnabled = false
// 设置动画开始时间,否则会有太多粒子
centerHeartLayer.beginTime = CACurrentMediaTime()
// 开始生成粒子
centerHeartLayer.birthRate = 1
// 一段时间后停止生成粒子
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [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
}
}

向上发射一个粒子动画效果

给控制器添加类型为 CAEmitterLayer 的属性 leftHeartLayer,在 viewDidLoad 方法中对此属性进行初始化

private var leftHeartLayer: CAEmitterLayer!

private func setupLeftHeartLayer() {
leftHeartLayer = CAEmitterLayer()
// 点状发射器,默认发射方向向右
// 这句可以省略,点状是默认值
leftHeartLayer.emitterShape = kCAEmitterLayerPoint
// 从发射器中的一点发射粒子
// 这句可以省略,是默认值
leftHeartLayer.emitterMode = kCAEmitterLayerVolume
leftHeartLayer.renderMode = kCAEmitterLayerOldestFirst
// 发射器位置
// 对于点状发射器,发射点在(emitterPosition.x, emitterPosition.y, emitterZPosition)
leftHeartLayer.emitterPosition = CGPoint(x: view.bounds.midX * 0.5, y: view.bounds.midY)
leftHeartLayer.birthRate = 0 let cell = CAEmitterCell()
cell.contents = #imageLiteral(resourceName: "Heart_red").cgImage
cell.scale = 0.5
cell.lifetime = 1
// 1秒发射1个粒子
cell.birthRate = 1
cell.alphaSpeed = -1
cell.velocity = 50
cell.emissionLongitude = -CGFloat.pi / 2 leftHeartLayer.emitterCells = [cell]
view.layer.addSublayer(leftHeartLayer)
}

点击按钮开始动画

@IBAction func leftHeartButtonClicked(_ sender: UIButton) {
sender.isUserInteractionEnabled = false
// 从上1秒开始动画,使按钮点击后立即发射粒子
leftHeartLayer.beginTime = CACurrentMediaTime() - 1
leftHeartLayer.birthRate = 1
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
guard let strongSelf = self else { return }
strongSelf.leftHeartLayer.birthRate = 0
}
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
guard self != nil else { return }
sender.isUserInteractionEnabled = true
}
}

向上发射几个粒子动画效果

给控制器添加类型为 CAEmitterLayer 的属性 rightHeartLayer,在 viewDidLoad 方法中对此属性进行初始化

private var rightHeartLayer: CAEmitterLayer!

private func setupRightHeartLayer() {
rightHeartLayer = CAEmitterLayer()
rightHeartLayer.renderMode = kCAEmitterLayerOldestFirst
rightHeartLayer.emitterPosition = CGPoint(x: view.bounds.midX * 1.5, y: view.bounds.midY)
rightHeartLayer.birthRate = 0 let cell = CAEmitterCell()
cell.contents = #imageLiteral(resourceName: "Heart_red").cgImage
cell.scale = 0.5
cell.lifetime = 1
cell.birthRate = 5
cell.alphaSpeed = -1
cell.velocity = 50
cell.emissionLongitude = -CGFloat.pi / 2
// 粒子发射角度的变化范围
cell.emissionRange = CGFloat.pi / 4 rightHeartLayer.emitterCells = [cell]
view.layer.addSublayer(rightHeartLayer)
}

点击按钮开始动画

@IBAction func rightHeartButtonClicked(_ sender: UIButton) {
sender.isUserInteractionEnabled = false
// 1秒发射5个粒子,0.2秒发射1个粒子,从上0.2秒开始动画,使按钮点击后立即发射粒子
rightHeartLayer.beginTime = CACurrentMediaTime() - 0.2
rightHeartLayer.birthRate = 1
DispatchQueue.main.asyncAfter(deadline: .now() + 0.8) { [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
}
}

抛物线粒子动画效果

实现抛物线动画需要给粒子加上重力加速度。此外,这里还加入粒子旋转效果,同时发射两种粒子。

给控制器添加类型为 CAEmitterLayer 的属性 gravityLayer,在 viewDidLoad 方法中对此属性进行初始化

private var gravityLayer: CAEmitterLayer!

private func setupGravityLayer() {
gravityLayer = CAEmitterLayer()
gravityLayer.renderMode = kCAEmitterLayerOldestFirst
gravityLayer.emitterPosition = CGPoint(x: 0, y: view.bounds.maxY)
gravityLayer.birthRate = 0 let cell = CAEmitterCell()
cell.contents = #imageLiteral(resourceName: "Heart_red").cgImage
cell.scale = 0.5
cell.lifetime = 10
cell.alphaSpeed = -0.1
cell.birthRate = 10
cell.velocity = 100
// y轴方法的加速度,模拟重力加速度
cell.yAcceleration = 20
cell.emissionLongitude = -CGFloat.pi / 4
cell.emissionRange = CGFloat.pi / 4
// 粒子旋转角速度,单位是弧度/秒,正值表示顺时针旋转
// 这句可以省略,默认值是零
cell.spin = 0
// 粒子旋转角速度变化范围
cell.spinRange = CGFloat.pi * 2 let cell2 = CAEmitterCell()
cell2.contents = #imageLiteral(resourceName: "Heart_blue").cgImage
cell2.scale = 0.3
cell2.lifetime = 20
cell2.alphaSpeed = -0.05
cell2.birthRate = 5
cell2.velocity = 135
cell2.yAcceleration = 20
cell2.emissionLongitude = -CGFloat.pi / 4
cell2.emissionRange = CGFloat.pi / 4
cell2.spin = 0
cell2.spinRange = CGFloat.pi * 2 // 图层要发射2种粒子
gravityLayer.emitterCells = [cell, cell2]
view.layer.addSublayer(gravityLayer)
}

点击开始或停止动画

@IBAction func gravityButtonClicked(_ sender: UIButton) {
if gravityLayer.birthRate == 0 {
gravityLayer.beginTime = CACurrentMediaTime()
gravityLayer.birthRate = 1
} else {
gravityLayer.birthRate = 0
}
}

以上是动画的实现方法,代码已上传 GitHub:https://github.com/Silence-GitHub/CoreAnimationDemo

转载请注明出处:http://www.cnblogs.com/silence-cnblogs/p/6971533.html

iOS CAEmitterLayer 实现粒子发射动画效果的更多相关文章

  1. IOS开发-UIView之动画效果的实现方法(合集)

    http://www.cnblogs.com/GarveyCalvin/p/4193963.html 前言:在开发APP中,我们会经常使用到动画效果.使用动画可以让我们的APP更酷更炫,最重要的是优化 ...

  2. ios学习--详解IPhone动画效果类型及实现方法

    详解IPhone动画效果类型及实现方法是本文要介绍的内容,主要介绍了iphone中动画的实现方法,不多说,我们一起来看内容. 实现iphone漂亮的动画效果主要有两种方法,一种是UIView层面的,一 ...

  3. QQ(iOS)客户端的粘性动画效果

    qq的app中要是有新的联系人发消息过来,相应联系人的cell右边会有一个红色的圆圈表示消息条数.如果去触碰那个圆圈,可以发现它竟然会跟着手指的移动而移动. 在一定范围内,手指离开屏幕,会发现红色圆圈 ...

  4. iOS开发之瞬间位移动画效果

    步骤:1.使用single view application 创建一个新的项目 2.在.h文件中遵守<UIGestureRecognizerDelegate>协议,创建一个UIimagev ...

  5. ios中仿蚂蚁森林动画效果

    参考链接:https://www.jianshu.com/p/0ba9d80f8e77 demo下载链接:https://gitee.com/ovix/TreeWithRandomFruitBtn

  6. iOS开发 QQ粘性动画效果

    QQ(iOS)客户端的粘性动画效果 时间 2016-02-17 16:50:00  博客园精华区 原文  http://www.cnblogs.com/ziyi--caolu/p/5195615.ht ...

  7. iOS的GIF动画效果实现

    引言:GIF图像格式是常见的一种动态图片格式,无论是在Web端还是在移动端都经常遇到,但是考虑目前iOS还无法原生展现GIF图片,而对于GIF的原生支持暂时也没有像JPG.PNG等图像格式支持得这么全 ...

  8. iOS CATransition 自定义转场动画

    https://www.jianshu.com/p/39c051cfe7dd CATransition CATransition 是CAAnimation的子类(如下图所示),用于控制器和控制器之间的 ...

  9. 关于粒子发射(CAEmitterLayer)

    技术是条长而远的路,只有不断学习丰富自己的技能才能让自己行走在路上! CAEmitterCell CAEmitterCell: CAEmitterCell是粒子发射系统里的粒子,用CAEmitterC ...

随机推荐

  1. 从源码角度入手实现RecyclerView的Item点击事件

    RecyclerView 作为 ListView 和 GridView 的替代产物,相信在Android界已广为流传. RecyclerView 本是不会有类似 ListView 的那种点击事件,但是 ...

  2. 原生js实现数据双向绑定

    最近接触了vue,在谈到vue等等的mvvm框架之前,先了解什么是数据双向绑定以及如何利用原生JS实现数据双向绑定 单向数据绑定 指先把模板写好,然后把模板和数据(数据可能来自后台)整合到一起形成HT ...

  3. 图零直播新闻发布会—TOLINK2.0全面上线

    在网络直播时代和现代信息技术条件下,教务管理正在由传统管理方式向数字化管理模式转变.教务管理创新需要现代信息技术来实现,使得教务管理的质量和效率得到了质的飞跃.图零直播,中国IT在线直播教育引领者,在 ...

  4. 蓝桥杯-买不到的数目-java

    /* (程序头部注释开始) * 程序的版权和版本声明部分 * Copyright (c) 2016, 广州科技贸易职业学院信息工程系学生 * All rights reserved. * 文件名称: ...

  5. 关于获取URL中传值的解决方法

    在我们页面的URL中包含着很多信息,包括域名,协议等等这里就不一一介绍了),对于我们开发者而言,使用比较多的就是页面之间的传值.为什么要页面传值呢?很简单,当你在浏览一个商品页面的时候你要看到一个商品 ...

  6. ESXi5.0误删除虚拟机还有办法恢复吗?答案是可以!

    [数据恢复故障描述]故障的虚拟化系统是 ESXi5.0,连接了多个LUN,其中一个1T的LUN上跑有7 台虚拟机,均为Windows Server 2003,管理员因为其它原因误删除了一台虚拟机,此台 ...

  7. Mycil命令行MySQL语法高亮和自动补全工具

    MyCli 是MySQL,MariaDB和Percona的命令行界面,具有自动完成和语法高亮的功能. 其效果如图: 那么我们应该怎么安装它呢,这里附上windows的安装方法. 在命令行下输入 pip ...

  8. 用java实现大文件分割、排序、合并

    import java.io.BufferedReader;   import java.io.BufferedWriter;   import java.io.FileNotFoundExcepti ...

  9. 新年伊始,.net菜鸟入院的第一篇随笔

    学习.net有半年了,大二一年都是微软校园的负责人,但是因为根本没有系统的学习过编程的知识,所以一直都是活动负责人的身份,忙忙碌碌也没有什么收获,大三一狠心就退了,想能够踏踏实实的敲敲代码,手上的学习 ...

  10. bash Shell条件测试

    3种测试命令: test EXPRESSION [ EXPRESSION ] [[ EXPRESSION ]]  注意:EXPRESSION前后必须有空白字符 bash的测试类型 数值测试: -eq: ...