You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
166 lines
4.7 KiB
166 lines
4.7 KiB
// |
|
// LTEmitterView.swift |
|
// |
|
// The MIT License (MIT) |
|
// Copyright (c) 2017 Lex Tang, |
|
// |
|
// Permission is hereby granted, free of charge, to any person obtaining a |
|
// copy of this software and associated documentation files |
|
// (the “Software”), to deal in the Software without restriction, |
|
// including without limitation the rights to use, copy, modify, merge, |
|
// publish, distribute, sublicense, and/or sell copies of the Software, |
|
// and to permit persons to whom the Software is furnished to do so, |
|
// subject to the following conditions: |
|
// |
|
// The above copyright notice and this permission notice shall be included |
|
// in all copies or substantial portions of the Software. |
|
// |
|
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS |
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
// |
|
|
|
import UIKit |
|
|
|
private func < <T : Comparable>(lhs: T?, rhs: T?) -> Bool { |
|
switch (lhs, rhs) { |
|
case let (l?, r?): |
|
return l < r |
|
case (nil, _?): |
|
return true |
|
default: |
|
return false |
|
} |
|
} |
|
|
|
private func > <T : Comparable>(lhs: T?, rhs: T?) -> Bool { |
|
switch (lhs, rhs) { |
|
case let (l?, r?): |
|
return l > r |
|
default: |
|
return rhs < lhs |
|
} |
|
} |
|
|
|
public struct LTEmitter { |
|
|
|
let layer: CAEmitterLayer = { |
|
let layer = CAEmitterLayer() |
|
layer.emitterPosition = CGPoint(x: 10, y: 10) |
|
layer.emitterSize = CGSize(width: 10, height: 1) |
|
layer.renderMode = .unordered |
|
layer.emitterShape = .line |
|
return layer |
|
}() |
|
|
|
let cell: CAEmitterCell = { |
|
let cell = CAEmitterCell() |
|
cell.name = "sparkle" |
|
cell.birthRate = 150.0 |
|
cell.velocity = 50.0 |
|
cell.velocityRange = -80.0 |
|
cell.lifetime = 0.16 |
|
cell.lifetimeRange = 0.1 |
|
cell.emissionLongitude = CGFloat(Double.pi / 2 * 2.0) |
|
cell.emissionRange = CGFloat(Double.pi / 2 * 2.0) |
|
cell.scale = 0.1 |
|
cell.yAcceleration = 100 |
|
cell.scaleSpeed = -0.06 |
|
cell.scaleRange = 0.1 |
|
return cell |
|
}() |
|
|
|
public var duration: Float = 0.6 |
|
|
|
init(name: String, particleName: String, duration: Float) { |
|
cell.name = name |
|
self.duration = duration |
|
var image: UIImage? |
|
defer { |
|
cell.contents = image?.cgImage |
|
} |
|
|
|
image = UIImage(named: particleName) |
|
|
|
if image != nil { |
|
return |
|
} |
|
// Load from Framework |
|
image = UIImage( |
|
named: particleName, |
|
in: Bundle(for: LTMorphingLabel.self), |
|
compatibleWith: nil) |
|
} |
|
|
|
public func play() { |
|
if layer.emitterCells?.count > 0 { |
|
return |
|
} |
|
|
|
layer.emitterCells = [cell] |
|
let d = DispatchTime.now() + Double(Int64(duration * Float(NSEC_PER_SEC))) / Double(NSEC_PER_SEC) |
|
DispatchQueue.main.asyncAfter(deadline: d) { [weak layer] in |
|
layer?.birthRate = 0.0 |
|
} |
|
} |
|
|
|
public func stop() { |
|
if nil != layer.superlayer { |
|
layer.emitterCells = nil |
|
layer.removeFromSuperlayer() |
|
} |
|
} |
|
|
|
func update(_ configureClosure: LTEmitterConfigureClosure? = .none) -> LTEmitter { |
|
configureClosure?(layer, cell) |
|
return self |
|
} |
|
|
|
} |
|
|
|
public typealias LTEmitterConfigureClosure = (CAEmitterLayer, CAEmitterCell) -> Void |
|
|
|
open class LTEmitterView: UIView { |
|
|
|
open lazy var emitters: [String: LTEmitter] = { |
|
var _emitters = [String: LTEmitter]() |
|
return _emitters |
|
}() |
|
|
|
open func createEmitter( |
|
_ name: String, |
|
particleName: String, |
|
duration: Float, |
|
configureClosure: LTEmitterConfigureClosure? |
|
) -> LTEmitter { |
|
|
|
var emitter: LTEmitter |
|
if let e = emitters[name] { |
|
emitter = e |
|
} else { |
|
emitter = LTEmitter( |
|
name: name, |
|
particleName: particleName, |
|
duration: duration |
|
) |
|
|
|
configureClosure?(emitter.layer, emitter.cell) |
|
|
|
layer.addSublayer(emitter.layer) |
|
emitters.updateValue(emitter, forKey: name) |
|
} |
|
return emitter |
|
} |
|
|
|
open func removeAllEmitters() { |
|
emitters.forEach { |
|
$0.value.layer.removeFromSuperlayer() |
|
} |
|
emitters.removeAll(keepingCapacity: false) |
|
} |
|
|
|
}
|
|
|