当前位置: 移动技术网 > 移动技术>移动开发>IOS > iOS CAEmitterLayer实现粒子发射动画效果

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

2019年07月24日  | 移动技术网移动技术  | 我要评论

ios实现粒子发射动画效果图

代码已上传 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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网