IOS CAEmitter Layer Implementing Animation Effect of Particle Emission

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

Keywords: iOS github Attribute angular

Added by Joe689 on Mon, 24 Jun 2019 01:32:50 +0300