Skip to content

Commit

Permalink
refactor: iOS animations (#616)
Browse files Browse the repository at this point in the history
## 📜 Description

Moved animations code to the dedicated `animations` folder.

Followed [swift performance
tips](https://github.com/swiftlang/swift/blob/main/docs/OptimizationTips.rst)
to improve performance of the code a little bit.

## 💡 Motivation and Context

Keeping code in separate folders supports better structure of the code.

Adding `final`/`private` allows compiler to perform more internal
optimizations which in the end will improve the overall performance of
the lbirary.

## 📢 Changelog

<!-- High level overview of important changes -->
<!-- For example: fixed status bar manipulation; added new types
declarations; -->
<!-- If your changes don't affect one of platform/language below - then
remove this platform/language -->

### iOS

- moved animation code to `animations` folder;
- add `final`/`private` in appropriate palce;

## 🤔 How Has This Been Tested?

Tested on CI.

## 📝 Checklist

- [x] CI successfully passed
- [x] I added new mocks and corresponding unit-tests if library API was
changed
  • Loading branch information
kirillzyusko authored Oct 7, 2024
1 parent 9846bc6 commit 3e850de
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 21 deletions.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation
import QuartzCore

// swiftlint:disable identifier_name
public class SpringAnimation: KeyboardAnimation {
public final class SpringAnimation: KeyboardAnimation {
// internal variables
private let zeta: Double // Damping ratio
private let omega0: Double // Undamped angular frequency of the oscillator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ import QuartzCore
* This class calculates the progress of animations based on Bézier curve control points.
* For more details on the Bézier curves, see [Desmos Graph](https://www.desmos.com/calculator/eynenh1aga?lang=en).
*/
public class TimingAnimation: KeyboardAnimation {
public final class TimingAnimation: KeyboardAnimation {
private let p1: CGPoint
private let p2: CGPoint

init(animation: CABasicAnimation, fromValue: Double, toValue: Double) {
let timingFunction = animation.timingFunction
var controlPoints: [Float] = [0, 0, 0, 0]
var controlPoints: ContiguousArray<Float> = ContiguousArray([0, 0, 0, 0])
timingFunction?.getControlPoint(at: 1, values: &controlPoints[0])
timingFunction?.getControlPoint(at: 2, values: &controlPoints[2])
let p1 = CGPoint(x: CGFloat(controlPoints[0]), y: CGFloat(controlPoints[1]))
Expand All @@ -33,7 +33,20 @@ public class TimingAnimation: KeyboardAnimation {
super.init(fromValue: fromValue, toValue: toValue, animation: animation)
}

func bezier(t: CGFloat, valueForPoint: (CGPoint) -> CGFloat) -> CGFloat {
// public functions
override func valueAt(time: Double) -> Double {
let x = time * Double(speed)
let frames = (animation?.duration ?? 0.0) * Double(speed)
let fraction = min(x / frames, 1)
let t = findTForX(xTarget: fraction)

let progress = bezierY(t: t)

return fromValue + (toValue - fromValue) * CGFloat(progress)
}

// private functions
private func bezier(t: CGFloat, valueForPoint: (CGPoint) -> CGFloat) -> CGFloat {
let u = 1 - t
let tt = t * t
let uu = u * u
Expand All @@ -49,27 +62,15 @@ public class TimingAnimation: KeyboardAnimation {
return term1 + term2 + term3
}

func bezierY(t: CGFloat) -> CGFloat {
private func bezierY(t: CGFloat) -> CGFloat {
return bezier(t: t) { $0.y }
}

func bezierX(t: CGFloat) -> CGFloat {
private func bezierX(t: CGFloat) -> CGFloat {
return bezier(t: t) { $0.x }
}

// public functions
override func valueAt(time: Double) -> Double {
let x = time * Double(speed)
let frames = (animation?.duration ?? 0.0) * Double(speed)
let fraction = min(x / frames, 1)
let t = findTForX(xTarget: fraction)

let progress = bezierY(t: t)

return fromValue + (toValue - fromValue) * CGFloat(progress)
}

func findTForX(xTarget: CGFloat, epsilon: CGFloat = 0.0001, maxIterations: Int = 100) -> CGFloat {
private func findTForX(xTarget: CGFloat, epsilon: CGFloat = 0.0001, maxIterations: Int = 100) -> CGFloat {
var t: CGFloat = 0.5 // Start with an initial guess of t = 0.5
for _ in 0 ..< maxIterations {
let currentX = bezierX(t: t) // Compute the x-coordinate at t
Expand All @@ -84,7 +85,7 @@ public class TimingAnimation: KeyboardAnimation {
return t // Return the approximation of t
}

func bezierDerivative(t: CGFloat, valueForPoint: (CGPoint) -> CGFloat) -> CGFloat {
private func bezierDerivative(t: CGFloat, valueForPoint: (CGPoint) -> CGFloat) -> CGFloat {
let u = 1 - t
let uu = u * u
let tt = t * t
Expand All @@ -100,7 +101,7 @@ public class TimingAnimation: KeyboardAnimation {
return term1 + term2 + term3
}

func bezierXDerivative(t: CGFloat) -> CGFloat {
private func bezierXDerivative(t: CGFloat) -> CGFloat {
return bezierDerivative(t: t) { $0.x }
}
}
Expand Down

0 comments on commit 3e850de

Please sign in to comment.