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.
167 lines
4.7 KiB
167 lines
4.7 KiB
![]()
2 years ago
|
//
|
||
|
// 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)
|
||
|
}
|
||
|
|
||
|
}
|