mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-07-25 20:50:47 +00:00
249 lines
9.6 KiB
Swift
249 lines
9.6 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import Display
|
|
import SwiftSignalKit
|
|
|
|
final class TodoChecksView: UIView, PhoneDemoDecorationView {
|
|
private struct Particle {
|
|
var trackIndex: Int
|
|
var position: CGPoint
|
|
var scale: CGFloat
|
|
var alpha: CGFloat
|
|
var direction: CGPoint
|
|
var velocity: CGFloat
|
|
var color: UIColor
|
|
var currentTime: CGFloat
|
|
var lifeTime: CGFloat
|
|
|
|
init(
|
|
trackIndex: Int,
|
|
position: CGPoint,
|
|
scale: CGFloat,
|
|
alpha: CGFloat,
|
|
direction: CGPoint,
|
|
velocity: CGFloat,
|
|
color: UIColor,
|
|
currentTime: CGFloat,
|
|
lifeTime: CGFloat
|
|
) {
|
|
self.trackIndex = trackIndex
|
|
self.position = position
|
|
self.scale = scale
|
|
self.alpha = alpha
|
|
self.direction = direction
|
|
self.velocity = velocity
|
|
self.color = color
|
|
self.currentTime = currentTime
|
|
self.lifeTime = lifeTime
|
|
}
|
|
|
|
mutating func update(deltaTime: CGFloat) {
|
|
var position = self.position
|
|
position.x += self.direction.x * self.velocity * deltaTime
|
|
position.y += self.direction.y * self.velocity * deltaTime
|
|
self.position = position
|
|
self.currentTime += deltaTime
|
|
}
|
|
}
|
|
|
|
private final class ParticleSet {
|
|
private let size: CGSize
|
|
private(set) var particles: [Particle] = []
|
|
|
|
init(size: CGSize, preAdvance: Bool) {
|
|
self.size = size
|
|
|
|
self.generateParticles(preAdvance: preAdvance)
|
|
}
|
|
|
|
private func generateParticles(preAdvance: Bool) {
|
|
let maxDirections = 16
|
|
|
|
if self.particles.count < maxDirections {
|
|
var allTrackIndices: [Int] = Array(repeating: 0, count: maxDirections)
|
|
for i in 0 ..< maxDirections {
|
|
allTrackIndices[i] = i
|
|
}
|
|
var takenIndexCount = 0
|
|
for particle in self.particles {
|
|
allTrackIndices[particle.trackIndex] = -1
|
|
takenIndexCount += 1
|
|
}
|
|
var availableTrackIndices: [Int] = []
|
|
availableTrackIndices.reserveCapacity(maxDirections - takenIndexCount)
|
|
for index in allTrackIndices {
|
|
if index != -1 {
|
|
availableTrackIndices.append(index)
|
|
}
|
|
}
|
|
|
|
if !availableTrackIndices.isEmpty {
|
|
availableTrackIndices.shuffle()
|
|
|
|
for takeIndex in availableTrackIndices {
|
|
let directionIndex = takeIndex
|
|
var angle = (CGFloat(directionIndex % maxDirections) / CGFloat(maxDirections)) * CGFloat.pi * 2.0
|
|
var lifeTimeMultiplier = 1.0
|
|
|
|
var isUpOrDownSemisphere = false
|
|
if angle > CGFloat.pi / 7.0 && angle < CGFloat.pi - CGFloat.pi / 7.0 {
|
|
isUpOrDownSemisphere = true
|
|
} else if !"".isEmpty, angle > CGFloat.pi + CGFloat.pi / 7.0 && angle < 2.0 * CGFloat.pi - CGFloat.pi / 7.0 {
|
|
isUpOrDownSemisphere = true
|
|
}
|
|
|
|
if isUpOrDownSemisphere {
|
|
if CGFloat.random(in: 0.0 ... 1.0) < 0.2 {
|
|
lifeTimeMultiplier = 0.3
|
|
} else {
|
|
angle += CGFloat.random(in: 0.0 ... 1.0) > 0.5 ? CGFloat.pi / 1.6 : -CGFloat.pi / 1.6
|
|
angle += CGFloat.random(in: -0.2 ... 0.2)
|
|
lifeTimeMultiplier = 0.5
|
|
}
|
|
}
|
|
// if self.large {
|
|
// angle += CGFloat.random(in: -0.5 ... 0.5)
|
|
// }
|
|
|
|
let direction = CGPoint(x: cos(angle), y: sin(angle))
|
|
let velocity = CGFloat.random(in: 15.0 ..< 20.0)
|
|
let scale = 1.0
|
|
let lifeTime = CGFloat.random(in: 2.0 ... 3.5)
|
|
|
|
var position = CGPoint(x: self.size.width / 2.0, y: self.size.height / 2.0)
|
|
var initialOffset: CGFloat = 0.5
|
|
if preAdvance {
|
|
initialOffset = CGFloat.random(in: 0.5 ... 1.0)
|
|
} else {
|
|
let p = CGFloat.random(in: 0.0 ... 1.0)
|
|
if p < 0.5 {
|
|
initialOffset = CGFloat.random(in: 0.65 ... 1.0)
|
|
} else {
|
|
initialOffset = 0.5
|
|
}
|
|
}
|
|
position.x += direction.x * initialOffset * 225.0
|
|
position.y += direction.y * initialOffset * 225.0
|
|
|
|
let particle = Particle(
|
|
trackIndex: directionIndex,
|
|
position: position,
|
|
scale: scale,
|
|
alpha: 1.0,
|
|
direction: direction,
|
|
velocity: velocity,
|
|
color: .white,
|
|
currentTime: 0.0,
|
|
lifeTime: lifeTime * lifeTimeMultiplier
|
|
)
|
|
self.particles.append(particle)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func update(deltaTime: CGFloat) {
|
|
for i in (0 ..< self.particles.count).reversed() {
|
|
self.particles[i].update(deltaTime: deltaTime)
|
|
if self.particles[i].currentTime > self.particles[i].lifeTime {
|
|
self.particles.remove(at: i)
|
|
}
|
|
}
|
|
|
|
self.generateParticles(preAdvance: false)
|
|
}
|
|
}
|
|
|
|
private var displayLink: SharedDisplayLinkDriver.Link?
|
|
|
|
private var particleSet: ParticleSet?
|
|
private let particleImage: UIImage
|
|
private var particleLayers: [SimpleLayer] = []
|
|
|
|
private var size: CGSize?
|
|
private let large: Bool = false
|
|
|
|
override init(frame: CGRect) {
|
|
// if large {
|
|
// self.particleImage = generateTintedImage(image: UIImage(bundleImageName: "Peer Info/PremiumIcon"), color: .white)!.withRenderingMode(.alwaysTemplate)
|
|
// } else {
|
|
self.particleImage = generateTintedImage(image: UIImage(bundleImageName: "Premium/Stars/Particle"), color: .white)!.withRenderingMode(.alwaysTemplate)
|
|
// }
|
|
|
|
super.init(frame: frame)
|
|
|
|
self.particleSet = ParticleSet(size: frame.size, preAdvance: true)
|
|
|
|
self.displayLink = SharedDisplayLinkDriver.shared.add(framesPerSecond: .max, { [weak self] delta in
|
|
self?.update(deltaTime: CGFloat(delta))
|
|
})
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
preconditionFailure()
|
|
}
|
|
|
|
fileprivate func update(size: CGSize) {
|
|
self.size = size
|
|
}
|
|
|
|
private func update(deltaTime: CGFloat) {
|
|
guard let particleSet = self.particleSet else {
|
|
return
|
|
}
|
|
particleSet.update(deltaTime: deltaTime)
|
|
|
|
for i in 0 ..< particleSet.particles.count {
|
|
let particle = particleSet.particles[i]
|
|
|
|
let particleLayer: SimpleLayer
|
|
if i < self.particleLayers.count {
|
|
particleLayer = self.particleLayers[i]
|
|
particleLayer.isHidden = false
|
|
} else {
|
|
particleLayer = SimpleLayer()
|
|
particleLayer.contents = self.particleImage.cgImage
|
|
particleLayer.bounds = CGRect(origin: CGPoint(), size: self.particleImage.size)
|
|
self.particleLayers.append(particleLayer)
|
|
self.layer.addSublayer(particleLayer)
|
|
}
|
|
|
|
particleLayer.layerTintColor = particle.color.cgColor
|
|
|
|
particleLayer.position = particle.position
|
|
particleLayer.opacity = Float(particle.alpha)
|
|
|
|
let particleScale = min(1.0, particle.currentTime / 0.3) * min(1.0, (particle.lifeTime - particle.currentTime) / 0.2) * particle.scale
|
|
particleLayer.transform = CATransform3DMakeScale(particleScale, particleScale, 1.0)
|
|
}
|
|
if particleSet.particles.count < self.particleLayers.count {
|
|
for i in particleSet.particles.count ..< self.particleLayers.count {
|
|
self.particleLayers[i].isHidden = true
|
|
}
|
|
}
|
|
}
|
|
|
|
private var visible = false
|
|
func setVisible(_ visible: Bool) {
|
|
guard self.visible != visible else {
|
|
return
|
|
}
|
|
self.visible = visible
|
|
|
|
self.displayLink?.isPaused = !visible
|
|
|
|
// let transition = ContainedViewLayoutTransition.animated(duration: 0.3, curve: .linear)
|
|
// transition.updateAlpha(layer: self.containerView.layer, alpha: visible ? 1.0 : 0.0, completion: { [weak self] finished in
|
|
// if let strongSelf = self, finished && !visible && !strongSelf.visible {
|
|
// for view in strongSelf.containerView.subviews {
|
|
// view.removeFromSuperview()
|
|
// }
|
|
// }
|
|
// })
|
|
}
|
|
|
|
func resetAnimation() {
|
|
|
|
}
|
|
}
|