使用粒子動(dòng)畫(huà),讓你的APP下點(diǎn)兒雪

又是一年

廢話

最近氣溫驟降,分分鐘感覺(jué)要下雪的樣子,一想到雪,就想到過(guò)年了,一想到過(guò)年我就...

然而都過(guò)年了,我還沒(méi)掙到錢...

算了,還是想想雪吧…

如果能使用代碼,讓自己的App下點(diǎn)兒雪,想想都cool到?jīng)]朋友了

唉,不廢話了,先看看效果

效果

具體實(shí)現(xiàn)

代碼實(shí)現(xiàn)方面其實(shí)沒(méi)有啥技巧,咱們就直接上

要實(shí)現(xiàn)粒子動(dòng)畫(huà)需要使用一個(gè)叫CAEmitterLayer的類,這個(gè)東西在QuartzCore框架里面,是一個(gè)直接作用與layer層的框架

相關(guān)代碼:

class SnowView: UIView {

    override init(frame: CGRect) {
        super.init(frame: frame) 
        
        let emitter = self.layer as! CAEmitterLayer // 修改view的layer
        emitter.emitterPosition = CGPoint(x: bounds.size.width/2, y: 0) // 發(fā)射粒子的位置
        emitter.emitterSize = bounds.size // 范圍
        emitter.emitterShape = kCAEmitterLayerRectangle // 粒子形狀
        
        let emitterCell = CAEmitterCell() // 創(chuàng)建粒子
        emitterCell.contents = UIImage(named: "flake")!.cgImage // 載入粒子圖片
        emitterCell.birthRate = 200 // 每秒釋放多少個(gè)粒子
        emitterCell.lifetime = 3.5 // 每個(gè)粒子的生命周期
        emitterCell.color = UIColor.white.cgColor // 粒子的顏色
        emitterCell.redRange = 0.0 // RGBA設(shè)置
        emitterCell.blueRange = 0.1
        emitterCell.greenRange = 0.0
        emitterCell.alphaRange = 0.5
        emitterCell.velocity = 9.8 // 重力加速度也就是物理里面G
        emitterCell.velocityRange = 350 // 加速范圍
        emitterCell.emissionRange = CGFloat(M_PI_2) // 下落是旋轉(zhuǎn)的角度
        emitterCell.emissionLongitude = CGFloat(-M_PI) //
        emitterCell.yAcceleration = 70 // 發(fā)射速度
        emitterCell.xAcceleration = 0  
        emitterCell.scale = 0.33 
        emitterCell.scaleRange = 1.25
        emitterCell.scaleSpeed = -0.25
        emitterCell.alphaRange = 0.5
        emitterCell.alphaSpeed = -0.15
        
        emitter.emitterCells = [emitterCell] // 載入
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override class var layerClass: AnyClass {
        return CAEmitterLayer.self
    }
}

上面這段代碼,是雪花粒子動(dòng)畫(huà)的完整實(shí)現(xiàn),注釋寫(xiě)的很清楚,這里需要注意的就是后面這幾句

override class var layerClass: AnyClass {
    return CAEmitterLayer.self
}

這里一定要重載layer屬性,不然開(kāi)頭第一句的強(qiáng)轉(zhuǎn)會(huì)崩潰,你懂的

let emitter = self.layer as! CAEmitterLayer

行了,下雪了...

根據(jù)需求改變

可如果代碼就這么實(shí)現(xiàn),就安心了,太不符合程序員的逼格了

在實(shí)際的開(kāi)發(fā)中可能會(huì)遇到各種問(wèn)題:

比如產(chǎn)品經(jīng)理說(shuō):我要做到想在哪里下雪就在那里下雪...

WTF!!!

產(chǎn)品經(jīng)理就是這么任性

怎么辦?

通常的處理方法有以下幾種:

  • 子類化
  • 寫(xiě)到分類/擴(kuò)展里面

如果你能想到子類化,說(shuō)明你是有面向?qū)ο笏枷氲?當(dāng)然有沒(méi)有對(duì)象就不知道了??)

如果你能想寫(xiě)到分類里面,說(shuō)明你對(duì)iOS編程已經(jīng)有些入門了

但是我想說(shuō),以上兩種方法都是最佳的解決辦法.

為啥不用繼承,請(qǐng)出門左轉(zhuǎn)看看這篇文章

為啥不寫(xiě)成分類/擴(kuò)展,請(qǐng)出門右轉(zhuǎn)...

嗯?那個(gè)同學(xué)你回來(lái),這個(gè)不需要出門右轉(zhuǎn),直接跟你說(shuō)為啥

其實(shí),寫(xiě)到分類/擴(kuò)展里面是個(gè)不錯(cuò)的選擇,但是隨著開(kāi)發(fā)的進(jìn)行,你會(huì)發(fā)現(xiàn)分類/擴(kuò)展里面的東西會(huì)越來(lái)越多,慢慢的成了一個(gè)大熔爐,啥東西都放在里面,最后成了一個(gè)垃圾桶,難以維護(hù),甚至到最后你自己都找不到自己之前寫(xiě)的東西在哪里,再最后,你連自己是否寫(xiě)過(guò)這個(gè)方法都不知道了...

這里最佳的實(shí)現(xiàn)方法是,使用面向協(xié)議的方法解決這個(gè)問(wèn)題,這樣不但清晰,而且更swift

面向協(xié)議

至于啥是面向協(xié)議,請(qǐng)看下圖

百度和Google在向你招手

行了,直接上代碼

import Foundation
import UIKit
import QuartzCore

protocol Snow {}

extension Snow where Self: UIView {
    
    func createSnow() {
        let emitter = layer as! CAEmitterLayer
        emitter.emitterPosition = CGPoint(x: bounds.size.width / 2, y: 0)
        emitter.emitterSize = bounds.size
        emitter.emitterShape = kCAEmitterLayerRectangle
        
        let emitterCell = CAEmitterCell()
        emitterCell.contents = UIImage(named: "flake.png")!.cgImage
        emitterCell.birthRate = 200
        emitterCell.lifetime = 3.5
        emitterCell.color = UIColor.white.cgColor
        emitterCell.redRange = 0.0
        emitterCell.blueRange = 0.1
        emitterCell.greenRange = 0.0
        emitterCell.velocity = 10
        emitterCell.velocityRange = 350
        emitterCell.emissionRange = CGFloat(M_PI_2)
        emitterCell.emissionLongitude = CGFloat(-M_PI)
        emitterCell.yAcceleration = 70
        emitterCell.xAcceleration = 0
        emitterCell.scale = 0.33
        emitterCell.scaleRange = 1.25
        emitterCell.scaleSpeed = -0.25
        emitterCell.alphaRange = 0.5
        emitterCell.alphaSpeed = -0.15
        
        emitter.emitterCells = [emitterCell]
    }
}

這樣以后,你的view代碼就變成下面這樣簡(jiǎn)單明了

import UIKit

class SnowView: UIView, Snow {

    override init(frame: CGRect) {
        super.init(frame: frame)
        
        createSnow()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override class var layerClass: AnyClass {
        return CAEmitterLayer.self
    }
}

以后產(chǎn)品經(jīng)理想在那里下雪,你就讓那個(gè)view遵循Snow協(xié)議,然后調(diào)用creatSnow(),就行了,自豪的告訴產(chǎn)品,他的理想已經(jīng)實(shí)現(xiàn)

呃...

是不是跑題了?咱們不是在聊如何下雪么 ?怎么跑面向協(xié)議編程上去了?

行吧,不管如何,已經(jīng)可以愉快的下雪了,真的快過(guò)年了...

項(xiàng)目源碼奉上

然而,還沒(méi)有掙到錢...

生命不息,折騰不止...
I'm not a real coder, but i love it so much!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容