Skip to content

Instantly share code, notes, and snippets.

@edwardsanchez
Last active March 1, 2024 21:20
Show Gist options
  • Save edwardsanchez/9d0a411e58d97f3f521f0bc845205f52 to your computer and use it in GitHub Desktop.
Save edwardsanchez/9d0a411e58d97f3f521f0bc845205f52 to your computer and use it in GitHub Desktop.
Spring Animator
//: Playground - noun: a place where people can play
import UIKit
//Changing values
var initialVelocity: CGFloat = 0
var runningTime: CGFloat = 0
var myProperty: CGFloat = 0
//Resting position of spring
let endNumberValue: CGFloat = 300
//Set relaxation time (duration in seconds)
let relaxationTime: CGFloat = 1
//Spring constants
let dampingRatio: CGFloat = 0.9
//Only allow damping ratio between just above 0 and 1 (critically damped)
let clippedDampingRatio: CGFloat = min(1, max(dampingRatio, 0.01))
let mass: CGFloat = 1
let fractionOfAmplitude: CGFloat = 1500 //A spring never gets to 0 amplitude, it gets infinitely smaller. This fraction represents the perceived 0 point.
let logOfFraction: CGFloat = log(fractionOfAmplitude)
let stiffness: CGFloat = (mass * pow(logOfFraction, 2)) / (pow(relaxationTime, 2) * pow(clippedDampingRatio, 2))
let angularFrequency: CGFloat = sqrt(stiffness/mass)
let damping: CGFloat = 2 * mass * angularFrequency * clippedDampingRatio
internal func springFunction(_ currentValue: CGFloat, _ length: CGFloat, _ velocity: inout CGFloat) -> CGFloat {
let displacement = currentValue - length
let springForce = -stiffness * displacement // Hooke's Law (F = -kx) (where k is stiffness constant and x is displacement value)
let forceDamper = -damping * velocity // F = -dv (where d is damping constant and v is velocity)
let dampedForce = springForce + forceDamper
let acceleration = dampedForce / mass // a = F / m
let refreshRate: CGFloat = 1/60 //Hz
velocity += acceleration * refreshRate
let position = velocity * refreshRate
return position
}
public func animateProperty(_ property: inout CGFloat, _ runningTime: CGFloat) {
if runningTime < relaxationTime {
property += springFunction(property, endNumberValue, &initialVelocity)
} else {
//Set final value to exact value
property = endNumberValue
}
}
while runningTime <= relaxationTime {
//Put this inside a CADisplayLink updating the running time and your view origin will animate in a spring
animateProperty(&myProperty, runningTime)
runningTime += 1/60
print(myProperty)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment