mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
Channel statistics improvements
This commit is contained in:
183
submodules/GraphCore/Sources/Helpers/AnimationController.swift
Normal file
183
submodules/GraphCore/Sources/Helpers/AnimationController.swift
Normal file
@@ -0,0 +1,183 @@
|
||||
//
|
||||
// RangeAnimatedContainer.swift
|
||||
// GraphTest
|
||||
//
|
||||
// Created by Andrei Salavei on 3/12/19.
|
||||
// Copyright © 2019 Andrei Salavei. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
#if os(macOS)
|
||||
import Cocoa
|
||||
#else
|
||||
import UIKit
|
||||
#endif
|
||||
|
||||
protocol Animatable {
|
||||
static func valueBetween(start: Self, end: Self, offset: Double) -> Self
|
||||
}
|
||||
|
||||
enum TimeFunction {
|
||||
case linear
|
||||
case easeOut
|
||||
case easeIn
|
||||
|
||||
func profress(time: TimeInterval, duration: TimeInterval) -> TimeInterval {
|
||||
switch self {
|
||||
case .linear:
|
||||
return time / duration
|
||||
case .easeIn:
|
||||
return (pow(2, 10 * (time / duration - 1)) - 0.0009765625) * 1.0009775171065499
|
||||
|
||||
case .easeOut:
|
||||
return (-pow(2, -10 * time / duration)) + 1 * 1.0009775171065499
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AnimationController<AnimatableObject: Animatable> {
|
||||
|
||||
private(set) var isAnimating: Bool = false
|
||||
private(set) var animationDuration: TimeInterval = 0.0
|
||||
private(set) var currentTime: TimeInterval = 0.0
|
||||
|
||||
private(set) var start: AnimatableObject
|
||||
private(set) var end: AnimatableObject
|
||||
private(set) var current: AnimatableObject
|
||||
|
||||
var timeFunction: TimeFunction = .linear
|
||||
|
||||
var refreshClosure: (() -> Void)?
|
||||
// var updateClosure: ((AnimatableObject) -> Void)?
|
||||
var completionClosure: (() -> Void)?
|
||||
|
||||
init(current: AnimatableObject, refreshClosure: (() -> Void)?) {
|
||||
self.current = current
|
||||
self.start = current
|
||||
self.end = current
|
||||
self.refreshClosure = refreshClosure
|
||||
}
|
||||
|
||||
func animate(to: AnimatableObject, duration: TimeInterval, timeFunction: TimeFunction = .linear) {
|
||||
self.timeFunction = timeFunction
|
||||
currentTime = 0
|
||||
animationDuration = duration
|
||||
if animationDuration > 0 {
|
||||
start = current
|
||||
end = to
|
||||
isAnimating = true
|
||||
DisplayLinkService.shared.add(listner: self)
|
||||
} else {
|
||||
start = to
|
||||
end = to
|
||||
current = to
|
||||
isAnimating = false
|
||||
DisplayLinkService.shared.remove(listner: self)
|
||||
}
|
||||
refreshClosure?()
|
||||
}
|
||||
|
||||
func set(current: AnimatableObject) {
|
||||
self.start = current
|
||||
self.end = current
|
||||
self.current = current
|
||||
|
||||
animationDuration = 0.0
|
||||
currentTime = 0.0
|
||||
// updateClosure?(current)
|
||||
refreshClosure?()
|
||||
if isAnimating {
|
||||
isAnimating = false
|
||||
DisplayLinkService.shared.remove(listner: self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AnimationController: DisplayLinkListner {
|
||||
func update(delta: TimeInterval) {
|
||||
guard isAnimating else {
|
||||
DisplayLinkService.shared.remove(listner: self)
|
||||
return
|
||||
}
|
||||
|
||||
currentTime += delta
|
||||
if currentTime > animationDuration || animationDuration <= 0 {
|
||||
start = end
|
||||
current = end
|
||||
isAnimating = false
|
||||
animationDuration = 0.0
|
||||
currentTime = 0.0
|
||||
// updateClosure?(end)
|
||||
completionClosure?()
|
||||
refreshClosure?()
|
||||
DisplayLinkService.shared.remove(listner: self)
|
||||
} else {
|
||||
let offset = timeFunction.profress(time: currentTime, duration: animationDuration)
|
||||
current = AnimatableObject.valueBetween(start: start, end: end, offset: offset)
|
||||
// updateClosure?(current)
|
||||
refreshClosure?()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ClosedRange: Animatable where Bound: BinaryFloatingPoint {
|
||||
static func valueBetween(start: ClosedRange<Bound>, end: ClosedRange<Bound>, offset: Double) -> ClosedRange<Bound> {
|
||||
let castedOffset = Bound(offset)
|
||||
return ClosedRange(uncheckedBounds: (lower: start.lowerBound + (end.lowerBound - start.lowerBound) * castedOffset,
|
||||
upper: start.upperBound + (end.upperBound - start.upperBound) * castedOffset))
|
||||
}
|
||||
}
|
||||
|
||||
extension CGFloat: Animatable {
|
||||
static func valueBetween(start: CGFloat, end: CGFloat, offset: Double) -> CGFloat {
|
||||
return start + (end - start) * CGFloat(offset)
|
||||
}
|
||||
}
|
||||
|
||||
extension Double: Animatable {
|
||||
static func valueBetween(start: Double, end: Double, offset: Double) -> Double {
|
||||
return start + (end - start) * Double(offset)
|
||||
}
|
||||
}
|
||||
|
||||
extension Int: Animatable {
|
||||
static func valueBetween(start: Int, end: Int, offset: Double) -> Int {
|
||||
return start + Int(Double(end - start) * offset)
|
||||
}
|
||||
}
|
||||
|
||||
extension CGPoint: Animatable {
|
||||
static func valueBetween(start: CGPoint, end: CGPoint, offset: Double) -> CGPoint {
|
||||
return CGPoint(x: start.x + (end.x - start.x) * CGFloat(offset),
|
||||
y: start.y + (end.y - start.y) * CGFloat(offset))
|
||||
}
|
||||
}
|
||||
|
||||
extension CGRect: Animatable {
|
||||
static func valueBetween(start: CGRect, end: CGRect, offset: Double) -> CGRect {
|
||||
return CGRect(x: start.origin.x + (end.origin.x - start.origin.x) * CGFloat(offset),
|
||||
y: start.origin.y + (end.origin.y - start.origin.y) * CGFloat(offset),
|
||||
width: start.width + (end.width - start.width) * CGFloat(offset),
|
||||
height: start.height + (end.height - start.height) * CGFloat(offset))
|
||||
}
|
||||
}
|
||||
|
||||
struct NSColorContainer: Animatable {
|
||||
var color: GColor
|
||||
|
||||
static func valueBetween(start: NSColorContainer, end: NSColorContainer, offset: Double) -> NSColorContainer {
|
||||
return NSColorContainer(color: GColor.valueBetween(start: start.color, end: end.color, offset: offset))
|
||||
}
|
||||
}
|
||||
|
||||
extension GColor {
|
||||
static func valueBetween(start: GColor, end: GColor, offset: Double) -> GColor {
|
||||
let offsetF = CGFloat(offset)
|
||||
let startCIColor = makeCIColor(color: start)
|
||||
let endCIColor = makeCIColor(color: end)
|
||||
return GColor(red: startCIColor.red + (endCIColor.red - startCIColor.red) * offsetF,
|
||||
green: startCIColor.green + (endCIColor.green - startCIColor.green) * offsetF,
|
||||
blue: startCIColor.blue + (endCIColor.blue - startCIColor.blue) * offsetF,
|
||||
alpha: startCIColor.alpha + (endCIColor.alpha - startCIColor.alpha) * offsetF)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user