Files

151 lines
4.3 KiB
Swift

//
// SpinningProgressView.swift
// TGUIKit
//
import Cocoa
import SwiftSignalKit
private let alphaWhenStopped: CGFloat = 0.15
private let fadeMultiplier: CGFloat = 0.85
private var numberOfFins: Int = 12
private let fadeOutTime: TimeInterval = 0.7
public class SpinningProgressView: View {
public var color: NSColor = presentation.colors.text
public var currentValue: CGFloat = 0.0
public var maxValue: CGFloat = 0.0
private var currentPosition: Int
private var finColors: [NSColor] = Array<NSColor>(repeating: presentation.colors.text, count: numberOfFins)
private var isAnimating:Bool
private var animationTimer: SwiftSignalKit.Timer? = nil
private var isFadingOut: Bool
private var fadeOutStartTime: Date = Date()
public required init(frame frameRect: NSRect) {
currentPosition = 0
isAnimating = false
isFadingOut = false
color = presentation.colors.text
currentValue = 0.0
maxValue = 100.0
super.init(frame: frameRect)
}
public func setColor(_ value: NSColor) {
if color != value {
color = value
for i in 0 ..< numberOfFins {
let alpha = alphaValue(forPosition: i)
finColors[i] = color.withAlphaComponent(alpha)
}
needsDisplay = true
}
}
func alphaValue(forPosition position: Int) -> CGFloat {
var normalValue = pow(fadeMultiplier, CGFloat((position + currentPosition) % numberOfFins))
if isFadingOut {
let timeSinceStop = -fadeOutStartTime.timeIntervalSinceNow
normalValue *= CGFloat(fadeOutTime - timeSinceStop)
}
return normalValue
}
public func stopAnimation() {
isFadingOut = true
fadeOutStartTime = Date()
}
public func startAnimation() {
self.actuallyStartAnimation()
}
private func actuallyStopAnimation() {
isAnimating = false
isFadingOut = false
self.animationTimer?.invalidate()
self.animationTimer = nil
self.needsDisplay = true
}
private func actuallyStartAnimation() {
self.actuallyStopAnimation()
isAnimating = true
isFadingOut = false
currentPosition = 0
animationTimer = SwiftSignalKit.Timer(timeout: 0.04, repeat: true, completion: { [weak self] in
self?.updateTimer()
}, queue: .mainQueue())
animationTimer?.start()
self.updateTimer()
}
private func updateTimer() {
let minAlpha = alphaWhenStopped
for i in 0 ..< numberOfFins {
let newAlpha = max(alphaValue(forPosition: numberOfFins - i), minAlpha)
self.finColors[i] = color.withAlphaComponent(newAlpha)
}
if isFadingOut {
if fadeOutStartTime.timeIntervalSinceNow < -fadeOutTime {
self.actuallyStopAnimation()
}
}
self.needsDisplay = true
if !isFadingOut {
self.currentPosition = currentPosition + 1 % numberOfFins
}
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public override func draw(_ layer: CALayer, in ctx: CGContext) {
let size = bounds.size
let length = min(size.height, size.width)
ctx.translateBy(x: size.width / 2, y: size.height / 2)
autoreleasepool {
let path = CGMutablePath()
let lineWidth = 0.0859375 * length
let lineStart = 0.234375 * length
let lineEnd = 0.421875 * length
path.move(to: NSPoint(x: 0, y: lineStart))
path.addLine(to: NSPoint(x: 0, y: lineEnd))
for i in 0 ..< numberOfFins {
let c = isAnimating ? finColors[i] : color.withAlphaComponent(alphaWhenStopped)
ctx.setLineWidth(lineWidth)
ctx.setLineCap(.round)
ctx.setLineJoin(.round)
ctx.setStrokeColor(c.cgColor)
ctx.addPath(path)
ctx.closePath()
ctx.strokePath()
ctx.rotate(by: 2 * .pi / CGFloat(numberOfFins))
}
}
}
}