blob: 60016293a9f5de484adf47cfe18bec795e1d647c [file] [log] [blame]
//
// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
//
// @flow
// @format
//
#import "spring_animation.h"
#include <Foundation/Foundation.h>
@interface SpringAnimation ()
@property(nonatomic, assign) double zeta;
@property(nonatomic, assign) double omega0;
@property(nonatomic, assign) double omega1;
@property(nonatomic, assign) double v0;
@end
// Spring code adapted from React Native's Animation Library, see:
// https://github.com/facebook/react-native/blob/main/Libraries/Animated/animations/SpringAnimation.js
@implementation SpringAnimation
- (instancetype)initWithStiffness:(double)stiffness
damping:(double)damping
mass:(double)mass
initialVelocity:(double)initialVelocity
fromValue:(double)fromValue
toValue:(double)toValue {
self = [super init];
if (self) {
_stiffness = stiffness;
_damping = damping;
_mass = mass;
_initialVelocity = initialVelocity;
_fromValue = fromValue;
_toValue = toValue;
_zeta = _damping / (2 * sqrt(_stiffness * _mass)); // Damping ratio.
_omega0 = sqrt(_stiffness / _mass); // Undamped angular frequency of the oscillator.
_omega1 = _omega0 * sqrt(1.0 - _zeta * _zeta); // Exponential decay.
_v0 = -_initialVelocity;
}
return self;
}
- (double)curveFunction:(double)t {
const double x0 = _toValue - _fromValue;
double y;
if (_zeta < 1) {
// Under damped.
const double envelope = exp(-_zeta * _omega0 * t);
y = _toValue - envelope * (((_v0 + _zeta * _omega0 * x0) / _omega1) * sin(_omega1 * t) +
x0 * cos(_omega1 * t));
} else {
// Critically damped.
const double envelope = exp(-_omega0 * t);
y = _toValue - envelope * (x0 + (_v0 + _omega0 * x0) * t);
}
return y;
}
@end