CAEmitterCell 粒子动画

在 iOS 系统中,粒子系统有2部分组成:CAEmitterLayer 和 CAEmitterCell。

(1)CAEmitterLayer 为粒子发射图层。该图层主要用于控制粒子展现范围、粒子发射位置、粒子发射形状、渲染模式等属性。通过 CAEmitterCell 构建的发射单元都受到 CAEmitterLayer 图层节制,可以说粒子展现必须在 CAEmitterLayer 图层上实现。

(2)CAEmitterCell 粒子发射单元,用于对粒子系统中的单个粒子做更加精细的控制。比如控制粒子的移动速度、方向、范围。在 CAEmitterCell 类中提供类几十种粒子属性参数设置,所以结合这些属性可以制作各种炫酷的粒子特效动画。

粒子火焰效果

1.birthRate:表示火焰效果中每秒粒子的组成个数,个数越多,火焰越大越逼真。
2.velocity:表明当前粒子速度信息,不同的粒子速度控制火焰的燃烧剧烈程度以及火焰的燃烧方向。

还有很多的属性设置才能实现火焰的效果,下面直接贴上实现代码。

override func viewDidLoad() {
   super.viewDidLoad()
   self.view.backgroundColor = UIColor.black
   
   let emitterCell = CAEmitterCell()
   emitterCell.name = "fire"
   emitterCell.emissionLongitude  = CGFloat(M_PI)// emissionLongitude:x-y 平面的 发 射方向
   emitterCell.velocity			= -1// 粒子速度 负数表明向上燃烧
   emitterCell.velocityRange		= 50// 粒子速度范围
   emitterCell.emissionRange		= 1.1//周围发射角度
   emitterCell.yAcceleration		= -200// 粒子y方向的加速度分量
   emitterCell.scaleSpeed			= 0.3// 缩放比例 超大火苗
   emitterCell.color = UIColor(red: 0 ,green: 0.4 ,blue:0.2 ,alpha:0.1).cgColor
   emitterCell.contents = UIImage(named: "fire.png")!.cgImage
   
   let emitterLayer = CAEmitterLayer()
   emitterLayer.position = self.view.center// 粒子发射位置
   emitterLayer.emitterSize = CGSize(width: 50, height: 10)// 控制火苗大小
   emitterLayer.renderMode = kCAEmitterLayerAdditive
   emitterLayer.emitterMode = kCAEmitterLayerOutline// 控制发射源模式 即形状
   emitterLayer.emitterShape = kCAEmitterLayerLine
   emitterLayer.emitterCells = [emitterCell]

   self.view.layer.addSublayer(emitterLayer)
   
   emitterLayer.setValue(500, forKeyPath: "emitterCells.fire.birthRate")
   emitterLayer.setValue(1, forKeyPath: "emitterCells.fire.lifetime")
}

效果如下:

20170625149838384979586.gif

霓虹效果实现

霓虹效果实现和火焰燃烧类似,都是通过 iOS 粒子来进行描述的。

override func viewDidLoad() {
   super.viewDidLoad()
   self.view.backgroundColor = UIColor.black
   
   let emitterCell = CAEmitterCell()
   
   emitterCell.name = "nenolight"
   emitterCell.emissionLongitude  = CGFloat(M_PI*2)// emissionLongitude:x-y 平面的 发 射方向
   emitterCell.velocity = 50// 粒子速度
   emitterCell.velocityRange = 50// 粒子速度范围
   emitterCell.scaleSpeed = -0.2// 缩放比例 超大火苗
   emitterCell.scale = 0.1
   emitterCell.greenSpeed = -0.1
   emitterCell.redSpeed = -0.2
   emitterCell.blueSpeed = 0.1
   emitterCell.alphaSpeed = -0.2
   emitterCell.birthRate = 100
   emitterCell.lifetime = 4
   emitterCell.color = UIColor.white.cgColor
   emitterCell.contents = UIImage(named: "neonlight.png")!.cgImage
   
   let emitterLayer = CAEmitterLayer()

   emitterLayer.position = self.view.center// 粒子发射位置
   emitterLayer.emitterSize = CGSize(width: 2, height: 2)// 控制粒子大小
   emitterLayer.renderMode = kCAEmitterLayerBackToFront
   emitterLayer.emitterMode = kCAEmitterLayerOutline// 控制发射源模式 即形状
   emitterLayer.emitterShape = kCAEmitterLayerCircle
   
   emitterLayer.emitterCells = [emitterCell]

   self.view.layer.addSublayer(emitterLayer)

}

效果如下:

20170625149838418551319.gif

CoreAnimation:CAGradientLayer

CAGradientLayer 追本溯源

CAGradientLayer 可以拆解为3个主要部分:CA、Gradient、Layer。

  1. CA 为 CoreAnimation 的缩写。
  2. Gradient 为梯度的意思,描述了当前动画的特点,实现一些梯度功能的动画效果。比如位置的梯度变化、颜色的梯度渐变等。
  3. Layer表明当前动画并非直接作用于 UIView 显示层上,而是作用在 Layer 内容层。

光波效果实现原理分析

通过对动画的分解,基本上可以弄清楚动画的演变过程。

  1. 光波的执行方向。
  2. 光波的颜色梯度。
  3. 光波的"彗星拖尾"效果。

光波方向

光波从上到下的扫描效果:

// 设置光波起始位置和终止位置坐标
gradientLayer.startPoint = CGPoint(x:0,y:0)
gradientLayer.endPoint = CGPoint(x:1,y:0)

光波梯度

let color = UIColor(red:216/255.0,green:114/255.0,blue:213/255.0,alpha:1.0) // 粉色
let color1 = UIColor(red:61/255.0,green:226/255.0,blue:210/255.0,alpha:1.0) // 青色
// 设置光波颜色梯度
gradientLayer:colors = [UIColor.clear.cgColor,color1.cgColor,color.cgColor];

光波"彗星拖尾"效果

let color = UIColor(red:216/255.0,green:114/255.0,blue:213/255.0,alpha:1.0) // 粉色
let color1 = UIColor(red:61/255.0,green:226/255.0,blue:210/255.0,alpha:1.0) // 青色
// 设置光波颜色梯度
gradientLayer:colors = [UIColor.clear.cgColor,color1.cgColor,color.cgColor];
gradientLayer.locations = [0.0,0.1,0.2]

其中 locations 为颜色渐变比例数组。

光波扫描效果

let gradientAnimation:CABasicAnimation = CABasicAnimation()
gradientAnimation.keyPath = 'locations' // 动画属性
gradientAnimation.fromValue = [0.0,0.1,0.2]; // 动画属性变化起始状态
gradientAnimation.toValue = [0.8,0.9,1.0]; // 动画属性变化终止状态值
gradientAnimation.duration = 3.0; // 动画执行周期
gradientAnimation.repeatCount = 10; // 动画执行重复次数
gradientLayer.add(gradientAnimation,forKey:nil) // 将基础动画添加到 Layer 图层

指纹扫描动画

override func viewDidLoad() {
   super.viewDidLoad()
   // part1:设置指纹扫描背景图片
   let image:UIImage = UIImage(named: "unLock.jpg")!
   let imageView:UIImageView = UIImageView(image: image)
   imageView.contentMode = UIViewContentMode.scaleAspectFit
   imageView.frame = self.view.bounds
   imageView.center = self.view.center
   self.view.backgroundColor = UIColor.black
   self.view.addSubview(imageView)
   // part2:设置Layer图层属性
   let gradientLayer:CAGradientLayer = CAGradientLayer()
   gradientLayer.frame = CGRect(x: 105, y: 330, width: 200,height: 200)
   imageView.layer.addSublayer(gradientLayer)
   gradientLayer.startPoint = CGPoint(x: 0, y: 0)
   gradientLayer.endPoint = CGPoint(x: 0, y: 1)
   gradientLayer.colors = [UIColor.clear.cgColor,UIColor.white.cgColor,UIColor.clear.cgColor]
   gradientLayer.locations = [0.0,0.1,0.2]
   // part3:设置CABasicAnimation
   let gradientAnimation:CABasicAnimation = CABasicAnimation()
   gradientAnimation.keyPath = "locations"
   gradientAnimation.fromValue = [0.0,0.1,0.2];
   gradientAnimation.toValue = [0.8,0.9,1.0];
   gradientAnimation.duration = 3.0;
   gradientAnimation.repeatCount = 10;
   gradientLayer.add(gradientAnimation, forKey: nil)
   
}

效果如下:

20170625149838566182232.gif

音量条跳动效果

设计思路:

  1. 动画起始阶段:15根柱状 UIView 视图和一组随机颜色
  2. 动画进行阶段
  3. 动画结束阶段

动画起始阶段

func setcolorArray(){
   colorArray = NSMutableArray()
   let  color1:UIColor = UIColor(red: 255.0 / 255.0, green: 127.0 / 255.0, blue: 79.0 / 255.0, alpha: 1.0)
   let  color2:UIColor = UIColor(red: 138.0 / 255.0, green: 206.0 / 255.0, blue: 245.0 / 255.0, alpha: 1.0)
   let  color3:UIColor = UIColor(red: 216.0 / 255.0, green: 114.0 / 255.0, blue: 213.0 / 255.0, alpha: 1.0)
   let  color4:UIColor = UIColor(red: 51.0 / 255.0, green: 207.0 / 255.0, blue: 48.0 / 255.0, alpha: 1.0)
   let  color5:UIColor = UIColor(red: 102.0 / 255.0, green: 150.0 / 255.0, blue: 232.0 / 255.0, alpha: 1.0)
   let  color6:UIColor = UIColor(red: 255.0 / 255.0, green: 105.0 / 255.0, blue: 177.0 / 255.0, alpha: 1.0)
   let  color7:UIColor = UIColor(red: 187.0 / 255.0, green: 56.0 / 255.0, blue: 201.0 / 255.0, alpha: 1.0)
   let  color8:UIColor = UIColor(red: 255.0 / 255.0, green: 163.0 / 255.0, blue: 0.0 / 255.0, alpha: 1.0)
   let  color9:UIColor = UIColor(red: 203.0 / 255.0, green: 93.0 / 255.0, blue: 92.0 / 255.0, alpha: 1.0)
   let  color10:UIColor = UIColor(red: 61.0 / 255.0, green: 226.0 / 255.0, blue: 210.0 / 255.0, alpha: 1.0)
   let  color11:UIColor = UIColor(red: 25.0 / 255.0, green: 146.0 / 255.0, blue: 255.0 / 255.0, alpha: 1.0)
   colorArray.add(color1)
   colorArray.add(color2)
   colorArray.add(color3)
   colorArray.add(color4)
   colorArray.add(color5)
   colorArray.add(color6)
   colorArray.add(color7)
   colorArray.add(color8)
   colorArray.add(color9)
   colorArray.add(color10)
   colorArray.add(color11)
}
var audioBarNum:Int = 0
var gradientLayer:CAGradientLayer = CAGradientLayer()
var layerArray:NSMutableArray = NSMutableArray()
var colorArray:NSMutableArray = NSMutableArray()
override func viewDidLoad() {
   super.viewDidLoad()
   setcolorArray()
   self.view.backgroundColor = UIColor.black
   audioBarNum = 15;
   for i in 0...audioBarNum {
       let h:CGFloat = 150
       let w:CGFloat = (self.view.frame.size.width-10)/CGFloat(audioBarNum)
       let x:CGFloat = 20
       let y:CGFloat = 50
       let view:UIView = UIView(frame: CGRect(x: w*CGFloat(i)+x, y: y, width: w-x, height: h))
       self.view.addSubview(view)

       gradientLayer = CAGradientLayer()
       gradientLayer.frame = view.bounds;
       gradientLayer.startPoint = CGPoint(x: 0, y: 0);
       gradientLayer.endPoint = CGPoint(x: 0, y: 1);
       view.layer.addSublayer(gradientLayer)
       layerArray.add(gradientLayer)
   }
   Timer.scheduledTimer(timeInterval: 0.4, target: self, selector: #selector(ViewController.colorChange), userInfo: nil, repeats: true)
}

动画进行阶段

for i in 0...audioBarNum {
  let h:CGFloat = 150
  let w:CGFloat = (self.view.frame.size.width-10)/CGFloat(audioBarNum)
  let x:CGFloat = 20
  let y:CGFloat = 50
  let view:UIView = UIView(frame: CGRect(x: w*CGFloat(i)+x, y: y, width: w-x, height: h))
  self.view.addSubview(view)

  gradientLayer = CAGradientLayer()
  gradientLayer.frame = view.bounds;
  gradientLayer.startPoint = CGPoint(x: 0, y: 0);
  gradientLayer.endPoint = CGPoint(x: 0, y: 1);
  view.layer.addSublayer(gradientLayer)
  layerArray.add(gradientLayer)
}

func colorChange(){
   for layer in layerArray{
       let index:Int = Int(arc4random_uniform(11))
       let color:UIColor = colorArray.object(at: index) as! UIColor
       let colors = [
           UIColor.clear.cgColor,
           color.cgColor
       ]
       let layer = layer as! CAGradientLayer
       layer.colors = colors
       layer.locations = [0,1.0]
       let gradientAnimation:CABasicAnimation = CABasicAnimation()
       gradientAnimation.keyPath = "locations"
       let beginValue = Float(arc4random_uniform(11))/10.0
       gradientAnimation.fromValue = [beginValue,beginValue]
       gradientAnimation.toValue = [1.0,1.0]
       gradientAnimation.duration = 0.4
       layer.add(gradientAnimation, forKey: nil)
   }
}

动画结束

可以设置动画运行时间或者其他事件后关闭动画效果

效果如下:

20170625149838628934794.gif

本文代码1:点击查看

本文代码2:点击查看