Skip to content

Instantly share code, notes, and snippets.

@heestand-xyz
Last active November 30, 2023 01:09
Show Gist options
  • Save heestand-xyz/b85f9f6f85b8d85a7bbe4f79a51eae7f to your computer and use it in GitHub Desktop.
Save heestand-xyz/b85f9f6f85b8d85a7bbe4f79a51eae7f to your computer and use it in GitHub Desktop.
Animator with Easing
import Foundation
import CoreGraphics
#if os(iOS)
import UIKit
#endif
struct Animator {
enum AnimationEase {
case linear
case easeIn
case easeInOut
case easeOut
}
@discardableResult
static func animate(for duration: CGFloat,
ease: AnimationEase = .linear,
loop: @escaping (CGFloat) -> (),
done: (() -> ())? = nil) -> Timer {
let startTime: Date = .now
#if os(macOS)
let refreshRate: Double = {
let id = CGMainDisplayID()
guard let display = CGDisplayCopyDisplayMode(id) else { return 60 }
return display.refreshRate
}()
#elseif os(visionOS)
let refreshRate = 90.0
#else
let refreshRate = Double(UIScreen.main.maximumFramesPerSecond)
#endif
loop(0.0)
return .scheduledTimer(withTimeInterval: 1.0 / refreshRate, repeats: true) { timer in
let elapsedTime = CGFloat(-startTime.timeIntervalSinceNow)
let fraction = min(elapsedTime / duration, 1.0)
let easeFraction = switch ease {
case .linear:
fraction
case .easeIn:
cos(fraction * .pi / 2 - .pi) + 1
case .easeInOut:
cos(fraction * .pi - .pi) / 2 + 0.5
case .easeOut:
cos(fraction * .pi / 2 - .pi / 2)
}
loop(easeFraction)
if fraction == 1.0 {
done?()
timer.invalidate()
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment