IOS CAEmitter Layer Implementing Animation Effect of Particle Emission
Design sketch
The code has been uploaded to GitHub: https://github.com/Silence-GitHub/CoreAnimationDemo
The animation effect is realized by CAEmitter Layer. CAEmitter Layer displays particle emission animation, the specific particles are encapsulated by CAEmitter Cell. The code example shows how CAEmitter Layer can be used. For convenience, set CAEmitter Layer directly in the UIView Controller. If used in a project, it is sometimes reasonable to add CAEmitter Layer to the UIView, such as the custom click-and-praise button, to streamline the controller's code.
Rain animation effect
The rain here falls at a uniform speed and the density of the rain changes gradually.
Add an attribute rainLayer of type CAEmitterLayer to the controller and initialize it in the viewDidLoad method
private var rainLayer: CAEmitterLayer! private func setupRainLayer() { // Particle Emission Layer rainLayer = CAEmitterLayer() // Launcher shape is linear, default direction upward rainLayer.emitterShape = kCAEmitterLayerLine // Emitting particles from the emitter profile rainLayer.emitterMode = kCAEmitterLayerOutline // Priority rendering of old particles rainLayer.renderMode = kCAEmitterLayerOldestFirst // Launch position // For linear transmitters, the two ends of the line are respectively // (emitterPosition.x-emitterSize.width/2, emitterPosition.y, emitterZPosition) and // (emitterPosition.x + emitterSize.width/2, emitterPosition.y, emitterZPosition) rainLayer.emitterPosition = CGPoint(x: view.bounds.midX, y: 0) // Launcher size rainLayer.emitterSize = CGSize(width: view.bounds.width, height: 0) // The multiple of the particle formation rate, which does not emit at first, is set to zero. rainLayer.birthRate = 0 // Emitted particles let cell = CAEmitterCell() // Particle display content, CGImage settings, display pictures cell.contents = #imageLiteral(resourceName: "Heart_red").cgImage // Scaling multiple of particles cell.scale = 0.1 // Particle life, in seconds cell.lifetime = 5 // Particle formation rate, in one second, multiplied by CAEmitter Layer's birthRate cell.birthRate = 1000 // Particle velocity cell.velocity = 500 // Particle emission angle, positive indicating clockwise direction cell.emissionLongitude = CGFloat.pi // Layer to emit a particle rainLayer.emitterCells = [cell] // Adding Particle Emission Layer view.layer.addSublayer(rainLayer) }
Click the button to start or stop the animation. CABasic Animation is used to gradually change the multiple of particle formation rate to achieve the effect that rain becomes larger or smaller.
@IBAction func rainButtonClicked(_ sender: UIButton) { // Continuous calls to this method can affect the coherence of rain becoming larger or smaller, so it is forbidden to click the button continuously. sender.isUserInteractionEnabled = false // Gradual Variation of Particle Formation Rate let birthRateAnimation = CABasicAnimation(keyPath: "birthRate") birthRateAnimation.duration = 3 if rainLayer.birthRate == 0 { // Rain gets heavier birthRateAnimation.fromValue = 0 birthRateAnimation.toValue = 1 rainLayer.birthRate = 1 } else { // Less rain birthRateAnimation.fromValue = 1 birthRateAnimation.toValue = 0 rainLayer.birthRate = 0 } // Add animation rainLayer.add(birthRateAnimation, forKey: "birthRate") // Restore button clickable status after animation time DispatchQueue.main.asyncAfter(deadline: .now() + birthRateAnimation.duration) { [weak self] in guard self != nil else { return } sender.isUserInteractionEnabled = true } }
Launch a circle of particle animation effects
Add an attribute centerHeartLayer of type CAEmitterLayer to the controller and initialize it in the viewDidLoad method
private var centerHeartLayer: CAEmitterLayer! private func setupCenterHeartLayer() { centerHeartLayer = CAEmitterLayer() // The emitter is circular in shape and emits particles around by default. centerHeartLayer.emitterShape = kCAEmitterLayerCircle centerHeartLayer.emitterMode = kCAEmitterLayerOutline centerHeartLayer.renderMode = kCAEmitterLayerOldestFirst // Emitter Location // For circular launchers // The center of the circle is located at (emitterPosition.x, emitterPosition.y, emitterZPosition) // Radius 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 // The particle scaling factor decreases by 0.02 per second, and the particle size decreases gradually. cell.scaleSpeed = -0.02 // The transparency of particles decreases by 1 per second, and the particles become transparent gradually. cell.alphaSpeed = -1 cell.velocity = 30 centerHeartLayer.emitterCells = [cell] view.layer.addSublayer(centerHeartLayer) }
Click on the button to start the animation
@IBAction func centerHeartButtonClicked(_ sender: UIButton) { sender.isUserInteractionEnabled = false // Set the animation start time, otherwise there will be too many particles centerHeartLayer.beginTime = CACurrentMediaTime() // Begin to generate particles centerHeartLayer.birthRate = 1 // After a period of time, the formation of particles ceased. 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 } }
Launch up a particle animation effect
Add an attribute leftHeartLayer of type CAEmitterLayer to the controller and initialize it in the viewDidLoad method
private var leftHeartLayer: CAEmitterLayer! private func setupLeftHeartLayer() { leftHeartLayer = CAEmitterLayer() // Spot Launcher, Default Launch Direction to Right // This sentence can be omitted. The dot is the default value. leftHeartLayer.emitterShape = kCAEmitterLayerPoint // Emitting particles from a point in the launcher // This sentence can be omitted. It is the default value. leftHeartLayer.emitterMode = kCAEmitterLayerVolume leftHeartLayer.renderMode = kCAEmitterLayerOldestFirst // Emitter Location // For point emitters, the emitter locates at (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 // Emission of a particle in one second cell.birthRate = 1 cell.alphaSpeed = -1 cell.velocity = 50 cell.emissionLongitude = -CGFloat.pi / 2 leftHeartLayer.emitterCells = [cell] view.layer.addSublayer(leftHeartLayer) }
Click on the button to start the animation
@IBAction func leftHeartButtonClicked(_ sender: UIButton) { sender.isUserInteractionEnabled = false // Start animation from the last 1 second, so that the button clicks and immediately emits particles 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 } }
Launch up several particle animation effects
Add the attribute rightHeartLayer of type CAEmitterLayer to the controller and initialize it in the viewDidLoad method
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 // Range of Particle Emission Angle cell.emissionRange = CGFloat.pi / 4 rightHeartLayer.emitterCells = [cell] view.layer.addSublayer(rightHeartLayer) }
Click on the button to start the animation
@IBAction func rightHeartButtonClicked(_ sender: UIButton) { sender.isUserInteractionEnabled = false // Five particles are emitted in one second and one particle is emitted in 0.2 seconds. The animation starts in the last 0.2 seconds so that the button can emit particles immediately after clicking. 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 } }
Parabolic particle animation effect
To realize parabolic animation, we need to add gravitational acceleration to the particles. In addition, the effect of particle rotation is added, and two kinds of particles are emitted at the same time.
Add the attribute gravityLayer of type CAEmitterLayer to the controller and initialize it in the viewDidLoad method
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 // Acceleration of y-axis method and Simulation of gravity acceleration cell.yAcceleration = 20 cell.emissionLongitude = -CGFloat.pi / 4 cell.emissionRange = CGFloat.pi / 4 // The angular velocity of a particle in radians/seconds. The positive value represents clockwise rotation. // This sentence can be omitted. The default value is zero. cell.spin = 0 // Range of variation of particle rotation angular velocity 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 // Layer to emit two kinds of particles gravityLayer.emitterCells = [cell, cell2] view.layer.addSublayer(gravityLayer) }
Click Start or Stop Animation
@IBAction func gravityButtonClicked(_ sender: UIButton) { if gravityLayer.birthRate == 0 { gravityLayer.beginTime = CACurrentMediaTime() gravityLayer.birthRate = 1 } else { gravityLayer.birthRate = 0 } }
The above is the implementation of animation. The code has been uploaded to GitHub: https://github.com/Silence-GitHub/CoreAnimationDemo
For reprinting, please indicate the source: http://www.cnblogs.com/silence-cnblogs/p/6971533.html