react-native/Libraries/NativeAnimation/Drivers/RCTSpringAnimation.m

214 lines
5.8 KiB
Mathematica
Raw Normal View History

/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTSpringAnimation.h"
#import <UIKit/UIKit.h>
#import <React/RCTConvert.h>
#import <React/RCTDefines.h>
#import "RCTValueAnimatedNode.h"
@interface RCTSpringAnimation ()
@property (nonatomic, strong) NSNumber *animationId;
@property (nonatomic, strong) RCTValueAnimatedNode *valueNode;
@property (nonatomic, assign) BOOL animationHasBegun;
@property (nonatomic, assign) BOOL animationHasFinished;
@end
Add closed-form damped harmonic oscillator algorithm to Animated.spring Summary: As I was working on mimicking iOS animations for my ongoing work with `react-navigation`, one task I had was to match the "push from right" animation that is common in UINavigationController. I was able to grab the exact animation values for this animation with some LLDB magic, and found that the screen is animated using a `CASpringAnimation` with the parameters: - stiffness: 1000 - damping: 500 - mass: 3 After spending a considerable amount of time attempting to replicate the spring created with these values by CASpringAnimation by specifying values for tension and friction in the current `Animated.spring` implementation, I was unable to come up with mathematically equivalent values that could replicate the spring _exactly_. After doing some research, I ended up disassembling the QuartzCore framework, reading the assembly, and determined that Apple's implementation of `CASpringAnimation` does not use an integrated, numerical animation model as we do in Animated.spring, but instead solved for the closed form of the equations that govern damped harmonic oscillation (the differential equations themselves are [here](https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator), and a paper describing the math to arrive at the closed-form solution to the second-order ODE that describes the DHO is [here](http://planetmath.org/sites/default/files/texpdf/39745.pdf)). Though we can get the currently implemented RK4 integration close by tweaking some values, it is, the current model is at it's core, an approximation. It seemed that if I wanted to implement the `CASpringAnimation` behavior _exactly_, I needed to implement the analytical model (as is implemented in `CASpringAnimation`) in `Animated`. We add three new optional parameters to `Animated.spring` (to both the JS and native implementations): - `stiffness`, a value describing the spring's stiffness coefficient - `damping`, a value defining how the spring's motion should be damped due to the forces of friction (technically called the _viscous damping coefficient_). - `mass`, a value describing the mass of the object attached to the end of the simulated spring Just like if a developer were to specify `bounciness`/`speed` and `tension`/`friction` in the same config, specifying any of these new parameters while also specifying the aforementioned config values will cause an error to be thrown. ~Defaults for `Animated.spring` across all three implementations (JS/iOS/Android) stay the same, so this is intended to be *a non-breaking change*.~ ~If `stiffness`, `damping`, or `mass` are provided in the config, we switch to animating the spring with the new damped harmonic oscillator model (`DHO` as described in the code).~ We replace the old RK4 integration implementation with our new analytic implementation. Tension/friction nicely correspond directly to stiffness/damping with the mass of the spring locked at 1. This is intended to be *a non-breaking change*, but there may be very slight differences in people's springs (maybe not even noticeable to the naked eye), given the fact that this implementation is more accurate. The DHO animation algorithm will calculate the _position_ of the spring at time _t_ explicitly and in an analytical fashion, and use this calculation to update the animation's value. It will also analytically calculate the velocity at time _t_, so as to allow animated value tracking to continue to work as expected. Also, docs have been updated to cover the new configuration options (and also I added docs for Animated configuration options that were missing, such as `restDisplacementThreshold`, etc). Run tests. Run "Animated Gratuitous App" and "NativeAnimation" example in RNTester. Closes https://github.com/facebook/react-native/pull/15322 Differential Revision: D5794791 Pulled By: hramos fbshipit-source-id: 58ed9e134a097e321c85c417a142576f6a8952f8
2017-09-21 06:36:11 +00:00
const NSTimeInterval MAX_DELTA_TIME = 0.064;
@implementation RCTSpringAnimation
{
CGFloat _toValue;
CGFloat _fromValue;
BOOL _overshootClamping;
CGFloat _restDisplacementThreshold;
CGFloat _restSpeedThreshold;
Add closed-form damped harmonic oscillator algorithm to Animated.spring Summary: As I was working on mimicking iOS animations for my ongoing work with `react-navigation`, one task I had was to match the "push from right" animation that is common in UINavigationController. I was able to grab the exact animation values for this animation with some LLDB magic, and found that the screen is animated using a `CASpringAnimation` with the parameters: - stiffness: 1000 - damping: 500 - mass: 3 After spending a considerable amount of time attempting to replicate the spring created with these values by CASpringAnimation by specifying values for tension and friction in the current `Animated.spring` implementation, I was unable to come up with mathematically equivalent values that could replicate the spring _exactly_. After doing some research, I ended up disassembling the QuartzCore framework, reading the assembly, and determined that Apple's implementation of `CASpringAnimation` does not use an integrated, numerical animation model as we do in Animated.spring, but instead solved for the closed form of the equations that govern damped harmonic oscillation (the differential equations themselves are [here](https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator), and a paper describing the math to arrive at the closed-form solution to the second-order ODE that describes the DHO is [here](http://planetmath.org/sites/default/files/texpdf/39745.pdf)). Though we can get the currently implemented RK4 integration close by tweaking some values, it is, the current model is at it's core, an approximation. It seemed that if I wanted to implement the `CASpringAnimation` behavior _exactly_, I needed to implement the analytical model (as is implemented in `CASpringAnimation`) in `Animated`. We add three new optional parameters to `Animated.spring` (to both the JS and native implementations): - `stiffness`, a value describing the spring's stiffness coefficient - `damping`, a value defining how the spring's motion should be damped due to the forces of friction (technically called the _viscous damping coefficient_). - `mass`, a value describing the mass of the object attached to the end of the simulated spring Just like if a developer were to specify `bounciness`/`speed` and `tension`/`friction` in the same config, specifying any of these new parameters while also specifying the aforementioned config values will cause an error to be thrown. ~Defaults for `Animated.spring` across all three implementations (JS/iOS/Android) stay the same, so this is intended to be *a non-breaking change*.~ ~If `stiffness`, `damping`, or `mass` are provided in the config, we switch to animating the spring with the new damped harmonic oscillator model (`DHO` as described in the code).~ We replace the old RK4 integration implementation with our new analytic implementation. Tension/friction nicely correspond directly to stiffness/damping with the mass of the spring locked at 1. This is intended to be *a non-breaking change*, but there may be very slight differences in people's springs (maybe not even noticeable to the naked eye), given the fact that this implementation is more accurate. The DHO animation algorithm will calculate the _position_ of the spring at time _t_ explicitly and in an analytical fashion, and use this calculation to update the animation's value. It will also analytically calculate the velocity at time _t_, so as to allow animated value tracking to continue to work as expected. Also, docs have been updated to cover the new configuration options (and also I added docs for Animated configuration options that were missing, such as `restDisplacementThreshold`, etc). Run tests. Run "Animated Gratuitous App" and "NativeAnimation" example in RNTester. Closes https://github.com/facebook/react-native/pull/15322 Differential Revision: D5794791 Pulled By: hramos fbshipit-source-id: 58ed9e134a097e321c85c417a142576f6a8952f8
2017-09-21 06:36:11 +00:00
CGFloat _stiffness;
CGFloat _damping;
CGFloat _mass;
CGFloat _initialVelocity;
NSTimeInterval _animationStartTime;
NSTimeInterval _animationCurrentTime;
RCTResponseSenderBlock _callback;
CGFloat _lastPosition;
CGFloat _lastVelocity;
NSInteger _iterations;
NSInteger _currentLoop;
Add closed-form damped harmonic oscillator algorithm to Animated.spring Summary: As I was working on mimicking iOS animations for my ongoing work with `react-navigation`, one task I had was to match the "push from right" animation that is common in UINavigationController. I was able to grab the exact animation values for this animation with some LLDB magic, and found that the screen is animated using a `CASpringAnimation` with the parameters: - stiffness: 1000 - damping: 500 - mass: 3 After spending a considerable amount of time attempting to replicate the spring created with these values by CASpringAnimation by specifying values for tension and friction in the current `Animated.spring` implementation, I was unable to come up with mathematically equivalent values that could replicate the spring _exactly_. After doing some research, I ended up disassembling the QuartzCore framework, reading the assembly, and determined that Apple's implementation of `CASpringAnimation` does not use an integrated, numerical animation model as we do in Animated.spring, but instead solved for the closed form of the equations that govern damped harmonic oscillation (the differential equations themselves are [here](https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator), and a paper describing the math to arrive at the closed-form solution to the second-order ODE that describes the DHO is [here](http://planetmath.org/sites/default/files/texpdf/39745.pdf)). Though we can get the currently implemented RK4 integration close by tweaking some values, it is, the current model is at it's core, an approximation. It seemed that if I wanted to implement the `CASpringAnimation` behavior _exactly_, I needed to implement the analytical model (as is implemented in `CASpringAnimation`) in `Animated`. We add three new optional parameters to `Animated.spring` (to both the JS and native implementations): - `stiffness`, a value describing the spring's stiffness coefficient - `damping`, a value defining how the spring's motion should be damped due to the forces of friction (technically called the _viscous damping coefficient_). - `mass`, a value describing the mass of the object attached to the end of the simulated spring Just like if a developer were to specify `bounciness`/`speed` and `tension`/`friction` in the same config, specifying any of these new parameters while also specifying the aforementioned config values will cause an error to be thrown. ~Defaults for `Animated.spring` across all three implementations (JS/iOS/Android) stay the same, so this is intended to be *a non-breaking change*.~ ~If `stiffness`, `damping`, or `mass` are provided in the config, we switch to animating the spring with the new damped harmonic oscillator model (`DHO` as described in the code).~ We replace the old RK4 integration implementation with our new analytic implementation. Tension/friction nicely correspond directly to stiffness/damping with the mass of the spring locked at 1. This is intended to be *a non-breaking change*, but there may be very slight differences in people's springs (maybe not even noticeable to the naked eye), given the fact that this implementation is more accurate. The DHO animation algorithm will calculate the _position_ of the spring at time _t_ explicitly and in an analytical fashion, and use this calculation to update the animation's value. It will also analytically calculate the velocity at time _t_, so as to allow animated value tracking to continue to work as expected. Also, docs have been updated to cover the new configuration options (and also I added docs for Animated configuration options that were missing, such as `restDisplacementThreshold`, etc). Run tests. Run "Animated Gratuitous App" and "NativeAnimation" example in RNTester. Closes https://github.com/facebook/react-native/pull/15322 Differential Revision: D5794791 Pulled By: hramos fbshipit-source-id: 58ed9e134a097e321c85c417a142576f6a8952f8
2017-09-21 06:36:11 +00:00
NSTimeInterval _t; // Current time (startTime + dt)
}
- (instancetype)initWithId:(NSNumber *)animationId
config:(NSDictionary *)config
forNode:(RCTValueAnimatedNode *)valueNode
callBack:(nullable RCTResponseSenderBlock)callback
{
if ((self = [super init])) {
NSNumber *iterations = [RCTConvert NSNumber:config[@"iterations"]] ?: @1;
_animationId = animationId;
_toValue = [RCTConvert CGFloat:config[@"toValue"]];
_fromValue = valueNode.value;
Add closed-form damped harmonic oscillator algorithm to Animated.spring Summary: As I was working on mimicking iOS animations for my ongoing work with `react-navigation`, one task I had was to match the "push from right" animation that is common in UINavigationController. I was able to grab the exact animation values for this animation with some LLDB magic, and found that the screen is animated using a `CASpringAnimation` with the parameters: - stiffness: 1000 - damping: 500 - mass: 3 After spending a considerable amount of time attempting to replicate the spring created with these values by CASpringAnimation by specifying values for tension and friction in the current `Animated.spring` implementation, I was unable to come up with mathematically equivalent values that could replicate the spring _exactly_. After doing some research, I ended up disassembling the QuartzCore framework, reading the assembly, and determined that Apple's implementation of `CASpringAnimation` does not use an integrated, numerical animation model as we do in Animated.spring, but instead solved for the closed form of the equations that govern damped harmonic oscillation (the differential equations themselves are [here](https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator), and a paper describing the math to arrive at the closed-form solution to the second-order ODE that describes the DHO is [here](http://planetmath.org/sites/default/files/texpdf/39745.pdf)). Though we can get the currently implemented RK4 integration close by tweaking some values, it is, the current model is at it's core, an approximation. It seemed that if I wanted to implement the `CASpringAnimation` behavior _exactly_, I needed to implement the analytical model (as is implemented in `CASpringAnimation`) in `Animated`. We add three new optional parameters to `Animated.spring` (to both the JS and native implementations): - `stiffness`, a value describing the spring's stiffness coefficient - `damping`, a value defining how the spring's motion should be damped due to the forces of friction (technically called the _viscous damping coefficient_). - `mass`, a value describing the mass of the object attached to the end of the simulated spring Just like if a developer were to specify `bounciness`/`speed` and `tension`/`friction` in the same config, specifying any of these new parameters while also specifying the aforementioned config values will cause an error to be thrown. ~Defaults for `Animated.spring` across all three implementations (JS/iOS/Android) stay the same, so this is intended to be *a non-breaking change*.~ ~If `stiffness`, `damping`, or `mass` are provided in the config, we switch to animating the spring with the new damped harmonic oscillator model (`DHO` as described in the code).~ We replace the old RK4 integration implementation with our new analytic implementation. Tension/friction nicely correspond directly to stiffness/damping with the mass of the spring locked at 1. This is intended to be *a non-breaking change*, but there may be very slight differences in people's springs (maybe not even noticeable to the naked eye), given the fact that this implementation is more accurate. The DHO animation algorithm will calculate the _position_ of the spring at time _t_ explicitly and in an analytical fashion, and use this calculation to update the animation's value. It will also analytically calculate the velocity at time _t_, so as to allow animated value tracking to continue to work as expected. Also, docs have been updated to cover the new configuration options (and also I added docs for Animated configuration options that were missing, such as `restDisplacementThreshold`, etc). Run tests. Run "Animated Gratuitous App" and "NativeAnimation" example in RNTester. Closes https://github.com/facebook/react-native/pull/15322 Differential Revision: D5794791 Pulled By: hramos fbshipit-source-id: 58ed9e134a097e321c85c417a142576f6a8952f8
2017-09-21 06:36:11 +00:00
_lastPosition = 0;
_valueNode = valueNode;
_overshootClamping = [RCTConvert BOOL:config[@"overshootClamping"]];
_restDisplacementThreshold = [RCTConvert CGFloat:config[@"restDisplacementThreshold"]];
_restSpeedThreshold = [RCTConvert CGFloat:config[@"restSpeedThreshold"]];
Add closed-form damped harmonic oscillator algorithm to Animated.spring Summary: As I was working on mimicking iOS animations for my ongoing work with `react-navigation`, one task I had was to match the "push from right" animation that is common in UINavigationController. I was able to grab the exact animation values for this animation with some LLDB magic, and found that the screen is animated using a `CASpringAnimation` with the parameters: - stiffness: 1000 - damping: 500 - mass: 3 After spending a considerable amount of time attempting to replicate the spring created with these values by CASpringAnimation by specifying values for tension and friction in the current `Animated.spring` implementation, I was unable to come up with mathematically equivalent values that could replicate the spring _exactly_. After doing some research, I ended up disassembling the QuartzCore framework, reading the assembly, and determined that Apple's implementation of `CASpringAnimation` does not use an integrated, numerical animation model as we do in Animated.spring, but instead solved for the closed form of the equations that govern damped harmonic oscillation (the differential equations themselves are [here](https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator), and a paper describing the math to arrive at the closed-form solution to the second-order ODE that describes the DHO is [here](http://planetmath.org/sites/default/files/texpdf/39745.pdf)). Though we can get the currently implemented RK4 integration close by tweaking some values, it is, the current model is at it's core, an approximation. It seemed that if I wanted to implement the `CASpringAnimation` behavior _exactly_, I needed to implement the analytical model (as is implemented in `CASpringAnimation`) in `Animated`. We add three new optional parameters to `Animated.spring` (to both the JS and native implementations): - `stiffness`, a value describing the spring's stiffness coefficient - `damping`, a value defining how the spring's motion should be damped due to the forces of friction (technically called the _viscous damping coefficient_). - `mass`, a value describing the mass of the object attached to the end of the simulated spring Just like if a developer were to specify `bounciness`/`speed` and `tension`/`friction` in the same config, specifying any of these new parameters while also specifying the aforementioned config values will cause an error to be thrown. ~Defaults for `Animated.spring` across all three implementations (JS/iOS/Android) stay the same, so this is intended to be *a non-breaking change*.~ ~If `stiffness`, `damping`, or `mass` are provided in the config, we switch to animating the spring with the new damped harmonic oscillator model (`DHO` as described in the code).~ We replace the old RK4 integration implementation with our new analytic implementation. Tension/friction nicely correspond directly to stiffness/damping with the mass of the spring locked at 1. This is intended to be *a non-breaking change*, but there may be very slight differences in people's springs (maybe not even noticeable to the naked eye), given the fact that this implementation is more accurate. The DHO animation algorithm will calculate the _position_ of the spring at time _t_ explicitly and in an analytical fashion, and use this calculation to update the animation's value. It will also analytically calculate the velocity at time _t_, so as to allow animated value tracking to continue to work as expected. Also, docs have been updated to cover the new configuration options (and also I added docs for Animated configuration options that were missing, such as `restDisplacementThreshold`, etc). Run tests. Run "Animated Gratuitous App" and "NativeAnimation" example in RNTester. Closes https://github.com/facebook/react-native/pull/15322 Differential Revision: D5794791 Pulled By: hramos fbshipit-source-id: 58ed9e134a097e321c85c417a142576f6a8952f8
2017-09-21 06:36:11 +00:00
_stiffness = [RCTConvert CGFloat:config[@"stiffness"]];
_damping = [RCTConvert CGFloat:config[@"damping"]];
_mass = [RCTConvert CGFloat:config[@"mass"]];
_initialVelocity = [RCTConvert CGFloat:config[@"initialVelocity"]];
Add closed-form damped harmonic oscillator algorithm to Animated.spring Summary: As I was working on mimicking iOS animations for my ongoing work with `react-navigation`, one task I had was to match the "push from right" animation that is common in UINavigationController. I was able to grab the exact animation values for this animation with some LLDB magic, and found that the screen is animated using a `CASpringAnimation` with the parameters: - stiffness: 1000 - damping: 500 - mass: 3 After spending a considerable amount of time attempting to replicate the spring created with these values by CASpringAnimation by specifying values for tension and friction in the current `Animated.spring` implementation, I was unable to come up with mathematically equivalent values that could replicate the spring _exactly_. After doing some research, I ended up disassembling the QuartzCore framework, reading the assembly, and determined that Apple's implementation of `CASpringAnimation` does not use an integrated, numerical animation model as we do in Animated.spring, but instead solved for the closed form of the equations that govern damped harmonic oscillation (the differential equations themselves are [here](https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator), and a paper describing the math to arrive at the closed-form solution to the second-order ODE that describes the DHO is [here](http://planetmath.org/sites/default/files/texpdf/39745.pdf)). Though we can get the currently implemented RK4 integration close by tweaking some values, it is, the current model is at it's core, an approximation. It seemed that if I wanted to implement the `CASpringAnimation` behavior _exactly_, I needed to implement the analytical model (as is implemented in `CASpringAnimation`) in `Animated`. We add three new optional parameters to `Animated.spring` (to both the JS and native implementations): - `stiffness`, a value describing the spring's stiffness coefficient - `damping`, a value defining how the spring's motion should be damped due to the forces of friction (technically called the _viscous damping coefficient_). - `mass`, a value describing the mass of the object attached to the end of the simulated spring Just like if a developer were to specify `bounciness`/`speed` and `tension`/`friction` in the same config, specifying any of these new parameters while also specifying the aforementioned config values will cause an error to be thrown. ~Defaults for `Animated.spring` across all three implementations (JS/iOS/Android) stay the same, so this is intended to be *a non-breaking change*.~ ~If `stiffness`, `damping`, or `mass` are provided in the config, we switch to animating the spring with the new damped harmonic oscillator model (`DHO` as described in the code).~ We replace the old RK4 integration implementation with our new analytic implementation. Tension/friction nicely correspond directly to stiffness/damping with the mass of the spring locked at 1. This is intended to be *a non-breaking change*, but there may be very slight differences in people's springs (maybe not even noticeable to the naked eye), given the fact that this implementation is more accurate. The DHO animation algorithm will calculate the _position_ of the spring at time _t_ explicitly and in an analytical fashion, and use this calculation to update the animation's value. It will also analytically calculate the velocity at time _t_, so as to allow animated value tracking to continue to work as expected. Also, docs have been updated to cover the new configuration options (and also I added docs for Animated configuration options that were missing, such as `restDisplacementThreshold`, etc). Run tests. Run "Animated Gratuitous App" and "NativeAnimation" example in RNTester. Closes https://github.com/facebook/react-native/pull/15322 Differential Revision: D5794791 Pulled By: hramos fbshipit-source-id: 58ed9e134a097e321c85c417a142576f6a8952f8
2017-09-21 06:36:11 +00:00
_callback = [callback copy];
_lastPosition = _fromValue;
_lastVelocity = _initialVelocity;
_animationHasFinished = iterations.integerValue == 0;
_iterations = iterations.integerValue;
_currentLoop = 1;
}
return self;
}
RCT_NOT_IMPLEMENTED(- (instancetype)init)
- (void)startAnimation
{
_animationStartTime = _animationCurrentTime = -1;
_animationHasBegun = YES;
}
- (void)stopAnimation
{
_valueNode = nil;
if (_callback) {
_callback(@[@{
@"finished": @(_animationHasFinished)
}]);
}
}
- (void)stepAnimationWithTime:(NSTimeInterval)currentTime
{
if (!_animationHasBegun || _animationHasFinished) {
// Animation has not begun or animation has already finished.
return;
}
Add closed-form damped harmonic oscillator algorithm to Animated.spring Summary: As I was working on mimicking iOS animations for my ongoing work with `react-navigation`, one task I had was to match the "push from right" animation that is common in UINavigationController. I was able to grab the exact animation values for this animation with some LLDB magic, and found that the screen is animated using a `CASpringAnimation` with the parameters: - stiffness: 1000 - damping: 500 - mass: 3 After spending a considerable amount of time attempting to replicate the spring created with these values by CASpringAnimation by specifying values for tension and friction in the current `Animated.spring` implementation, I was unable to come up with mathematically equivalent values that could replicate the spring _exactly_. After doing some research, I ended up disassembling the QuartzCore framework, reading the assembly, and determined that Apple's implementation of `CASpringAnimation` does not use an integrated, numerical animation model as we do in Animated.spring, but instead solved for the closed form of the equations that govern damped harmonic oscillation (the differential equations themselves are [here](https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator), and a paper describing the math to arrive at the closed-form solution to the second-order ODE that describes the DHO is [here](http://planetmath.org/sites/default/files/texpdf/39745.pdf)). Though we can get the currently implemented RK4 integration close by tweaking some values, it is, the current model is at it's core, an approximation. It seemed that if I wanted to implement the `CASpringAnimation` behavior _exactly_, I needed to implement the analytical model (as is implemented in `CASpringAnimation`) in `Animated`. We add three new optional parameters to `Animated.spring` (to both the JS and native implementations): - `stiffness`, a value describing the spring's stiffness coefficient - `damping`, a value defining how the spring's motion should be damped due to the forces of friction (technically called the _viscous damping coefficient_). - `mass`, a value describing the mass of the object attached to the end of the simulated spring Just like if a developer were to specify `bounciness`/`speed` and `tension`/`friction` in the same config, specifying any of these new parameters while also specifying the aforementioned config values will cause an error to be thrown. ~Defaults for `Animated.spring` across all three implementations (JS/iOS/Android) stay the same, so this is intended to be *a non-breaking change*.~ ~If `stiffness`, `damping`, or `mass` are provided in the config, we switch to animating the spring with the new damped harmonic oscillator model (`DHO` as described in the code).~ We replace the old RK4 integration implementation with our new analytic implementation. Tension/friction nicely correspond directly to stiffness/damping with the mass of the spring locked at 1. This is intended to be *a non-breaking change*, but there may be very slight differences in people's springs (maybe not even noticeable to the naked eye), given the fact that this implementation is more accurate. The DHO animation algorithm will calculate the _position_ of the spring at time _t_ explicitly and in an analytical fashion, and use this calculation to update the animation's value. It will also analytically calculate the velocity at time _t_, so as to allow animated value tracking to continue to work as expected. Also, docs have been updated to cover the new configuration options (and also I added docs for Animated configuration options that were missing, such as `restDisplacementThreshold`, etc). Run tests. Run "Animated Gratuitous App" and "NativeAnimation" example in RNTester. Closes https://github.com/facebook/react-native/pull/15322 Differential Revision: D5794791 Pulled By: hramos fbshipit-source-id: 58ed9e134a097e321c85c417a142576f6a8952f8
2017-09-21 06:36:11 +00:00
// calculate delta time
NSTimeInterval deltaTime;
if(_animationStartTime == -1) {
_t = 0.0;
_animationStartTime = currentTime;
deltaTime = 0.0;
} else {
// Handle frame drops, and only advance dt by a max of MAX_DELTA_TIME
deltaTime = MIN(MAX_DELTA_TIME, currentTime - _animationCurrentTime);
_t = _t + deltaTime;
}
Add closed-form damped harmonic oscillator algorithm to Animated.spring Summary: As I was working on mimicking iOS animations for my ongoing work with `react-navigation`, one task I had was to match the "push from right" animation that is common in UINavigationController. I was able to grab the exact animation values for this animation with some LLDB magic, and found that the screen is animated using a `CASpringAnimation` with the parameters: - stiffness: 1000 - damping: 500 - mass: 3 After spending a considerable amount of time attempting to replicate the spring created with these values by CASpringAnimation by specifying values for tension and friction in the current `Animated.spring` implementation, I was unable to come up with mathematically equivalent values that could replicate the spring _exactly_. After doing some research, I ended up disassembling the QuartzCore framework, reading the assembly, and determined that Apple's implementation of `CASpringAnimation` does not use an integrated, numerical animation model as we do in Animated.spring, but instead solved for the closed form of the equations that govern damped harmonic oscillation (the differential equations themselves are [here](https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator), and a paper describing the math to arrive at the closed-form solution to the second-order ODE that describes the DHO is [here](http://planetmath.org/sites/default/files/texpdf/39745.pdf)). Though we can get the currently implemented RK4 integration close by tweaking some values, it is, the current model is at it's core, an approximation. It seemed that if I wanted to implement the `CASpringAnimation` behavior _exactly_, I needed to implement the analytical model (as is implemented in `CASpringAnimation`) in `Animated`. We add three new optional parameters to `Animated.spring` (to both the JS and native implementations): - `stiffness`, a value describing the spring's stiffness coefficient - `damping`, a value defining how the spring's motion should be damped due to the forces of friction (technically called the _viscous damping coefficient_). - `mass`, a value describing the mass of the object attached to the end of the simulated spring Just like if a developer were to specify `bounciness`/`speed` and `tension`/`friction` in the same config, specifying any of these new parameters while also specifying the aforementioned config values will cause an error to be thrown. ~Defaults for `Animated.spring` across all three implementations (JS/iOS/Android) stay the same, so this is intended to be *a non-breaking change*.~ ~If `stiffness`, `damping`, or `mass` are provided in the config, we switch to animating the spring with the new damped harmonic oscillator model (`DHO` as described in the code).~ We replace the old RK4 integration implementation with our new analytic implementation. Tension/friction nicely correspond directly to stiffness/damping with the mass of the spring locked at 1. This is intended to be *a non-breaking change*, but there may be very slight differences in people's springs (maybe not even noticeable to the naked eye), given the fact that this implementation is more accurate. The DHO animation algorithm will calculate the _position_ of the spring at time _t_ explicitly and in an analytical fashion, and use this calculation to update the animation's value. It will also analytically calculate the velocity at time _t_, so as to allow animated value tracking to continue to work as expected. Also, docs have been updated to cover the new configuration options (and also I added docs for Animated configuration options that were missing, such as `restDisplacementThreshold`, etc). Run tests. Run "Animated Gratuitous App" and "NativeAnimation" example in RNTester. Closes https://github.com/facebook/react-native/pull/15322 Differential Revision: D5794791 Pulled By: hramos fbshipit-source-id: 58ed9e134a097e321c85c417a142576f6a8952f8
2017-09-21 06:36:11 +00:00
// store the timestamp
_animationCurrentTime = currentTime;
Add closed-form damped harmonic oscillator algorithm to Animated.spring Summary: As I was working on mimicking iOS animations for my ongoing work with `react-navigation`, one task I had was to match the "push from right" animation that is common in UINavigationController. I was able to grab the exact animation values for this animation with some LLDB magic, and found that the screen is animated using a `CASpringAnimation` with the parameters: - stiffness: 1000 - damping: 500 - mass: 3 After spending a considerable amount of time attempting to replicate the spring created with these values by CASpringAnimation by specifying values for tension and friction in the current `Animated.spring` implementation, I was unable to come up with mathematically equivalent values that could replicate the spring _exactly_. After doing some research, I ended up disassembling the QuartzCore framework, reading the assembly, and determined that Apple's implementation of `CASpringAnimation` does not use an integrated, numerical animation model as we do in Animated.spring, but instead solved for the closed form of the equations that govern damped harmonic oscillation (the differential equations themselves are [here](https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator), and a paper describing the math to arrive at the closed-form solution to the second-order ODE that describes the DHO is [here](http://planetmath.org/sites/default/files/texpdf/39745.pdf)). Though we can get the currently implemented RK4 integration close by tweaking some values, it is, the current model is at it's core, an approximation. It seemed that if I wanted to implement the `CASpringAnimation` behavior _exactly_, I needed to implement the analytical model (as is implemented in `CASpringAnimation`) in `Animated`. We add three new optional parameters to `Animated.spring` (to both the JS and native implementations): - `stiffness`, a value describing the spring's stiffness coefficient - `damping`, a value defining how the spring's motion should be damped due to the forces of friction (technically called the _viscous damping coefficient_). - `mass`, a value describing the mass of the object attached to the end of the simulated spring Just like if a developer were to specify `bounciness`/`speed` and `tension`/`friction` in the same config, specifying any of these new parameters while also specifying the aforementioned config values will cause an error to be thrown. ~Defaults for `Animated.spring` across all three implementations (JS/iOS/Android) stay the same, so this is intended to be *a non-breaking change*.~ ~If `stiffness`, `damping`, or `mass` are provided in the config, we switch to animating the spring with the new damped harmonic oscillator model (`DHO` as described in the code).~ We replace the old RK4 integration implementation with our new analytic implementation. Tension/friction nicely correspond directly to stiffness/damping with the mass of the spring locked at 1. This is intended to be *a non-breaking change*, but there may be very slight differences in people's springs (maybe not even noticeable to the naked eye), given the fact that this implementation is more accurate. The DHO animation algorithm will calculate the _position_ of the spring at time _t_ explicitly and in an analytical fashion, and use this calculation to update the animation's value. It will also analytically calculate the velocity at time _t_, so as to allow animated value tracking to continue to work as expected. Also, docs have been updated to cover the new configuration options (and also I added docs for Animated configuration options that were missing, such as `restDisplacementThreshold`, etc). Run tests. Run "Animated Gratuitous App" and "NativeAnimation" example in RNTester. Closes https://github.com/facebook/react-native/pull/15322 Differential Revision: D5794791 Pulled By: hramos fbshipit-source-id: 58ed9e134a097e321c85c417a142576f6a8952f8
2017-09-21 06:36:11 +00:00
CGFloat c = _damping;
CGFloat m = _mass;
CGFloat k = _stiffness;
CGFloat v0 = -_initialVelocity;
CGFloat zeta = c / (2 * sqrtf(k * m));
CGFloat omega0 = sqrtf(k / m);
CGFloat omega1 = omega0 * sqrtf(1.0 - (zeta * zeta));
CGFloat x0 = _toValue - _fromValue;
CGFloat position;
CGFloat velocity;
if (zeta < 1) {
// Under damped
CGFloat envelope = expf(-zeta * omega0 * _t);
position =
_toValue -
envelope *
((v0 + zeta * omega0 * x0) / omega1 * sinf(omega1 * _t) +
x0 * cosf(omega1 * _t));
// This looks crazy -- it's actually just the derivative of the
// oscillation function
velocity =
zeta *
omega0 *
envelope *
(sinf(omega1 * _t) * (v0 + zeta * omega0 * x0) / omega1 +
x0 * cosf(omega1 * _t)) -
envelope *
(cosf(omega1 * _t) * (v0 + zeta * omega0 * x0) -
omega1 * x0 * sinf(omega1 * _t));
} else {
CGFloat envelope = expf(-omega0 * _t);
position = _toValue - envelope * (x0 + (v0 + omega0 * x0) * _t);
velocity =
envelope * (v0 * (_t * omega0 - 1) + _t * x0 * (omega0 * omega0));
}
Add closed-form damped harmonic oscillator algorithm to Animated.spring Summary: As I was working on mimicking iOS animations for my ongoing work with `react-navigation`, one task I had was to match the "push from right" animation that is common in UINavigationController. I was able to grab the exact animation values for this animation with some LLDB magic, and found that the screen is animated using a `CASpringAnimation` with the parameters: - stiffness: 1000 - damping: 500 - mass: 3 After spending a considerable amount of time attempting to replicate the spring created with these values by CASpringAnimation by specifying values for tension and friction in the current `Animated.spring` implementation, I was unable to come up with mathematically equivalent values that could replicate the spring _exactly_. After doing some research, I ended up disassembling the QuartzCore framework, reading the assembly, and determined that Apple's implementation of `CASpringAnimation` does not use an integrated, numerical animation model as we do in Animated.spring, but instead solved for the closed form of the equations that govern damped harmonic oscillation (the differential equations themselves are [here](https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator), and a paper describing the math to arrive at the closed-form solution to the second-order ODE that describes the DHO is [here](http://planetmath.org/sites/default/files/texpdf/39745.pdf)). Though we can get the currently implemented RK4 integration close by tweaking some values, it is, the current model is at it's core, an approximation. It seemed that if I wanted to implement the `CASpringAnimation` behavior _exactly_, I needed to implement the analytical model (as is implemented in `CASpringAnimation`) in `Animated`. We add three new optional parameters to `Animated.spring` (to both the JS and native implementations): - `stiffness`, a value describing the spring's stiffness coefficient - `damping`, a value defining how the spring's motion should be damped due to the forces of friction (technically called the _viscous damping coefficient_). - `mass`, a value describing the mass of the object attached to the end of the simulated spring Just like if a developer were to specify `bounciness`/`speed` and `tension`/`friction` in the same config, specifying any of these new parameters while also specifying the aforementioned config values will cause an error to be thrown. ~Defaults for `Animated.spring` across all three implementations (JS/iOS/Android) stay the same, so this is intended to be *a non-breaking change*.~ ~If `stiffness`, `damping`, or `mass` are provided in the config, we switch to animating the spring with the new damped harmonic oscillator model (`DHO` as described in the code).~ We replace the old RK4 integration implementation with our new analytic implementation. Tension/friction nicely correspond directly to stiffness/damping with the mass of the spring locked at 1. This is intended to be *a non-breaking change*, but there may be very slight differences in people's springs (maybe not even noticeable to the naked eye), given the fact that this implementation is more accurate. The DHO animation algorithm will calculate the _position_ of the spring at time _t_ explicitly and in an analytical fashion, and use this calculation to update the animation's value. It will also analytically calculate the velocity at time _t_, so as to allow animated value tracking to continue to work as expected. Also, docs have been updated to cover the new configuration options (and also I added docs for Animated configuration options that were missing, such as `restDisplacementThreshold`, etc). Run tests. Run "Animated Gratuitous App" and "NativeAnimation" example in RNTester. Closes https://github.com/facebook/react-native/pull/15322 Differential Revision: D5794791 Pulled By: hramos fbshipit-source-id: 58ed9e134a097e321c85c417a142576f6a8952f8
2017-09-21 06:36:11 +00:00
_lastPosition = position;
_lastVelocity = velocity;
Add closed-form damped harmonic oscillator algorithm to Animated.spring Summary: As I was working on mimicking iOS animations for my ongoing work with `react-navigation`, one task I had was to match the "push from right" animation that is common in UINavigationController. I was able to grab the exact animation values for this animation with some LLDB magic, and found that the screen is animated using a `CASpringAnimation` with the parameters: - stiffness: 1000 - damping: 500 - mass: 3 After spending a considerable amount of time attempting to replicate the spring created with these values by CASpringAnimation by specifying values for tension and friction in the current `Animated.spring` implementation, I was unable to come up with mathematically equivalent values that could replicate the spring _exactly_. After doing some research, I ended up disassembling the QuartzCore framework, reading the assembly, and determined that Apple's implementation of `CASpringAnimation` does not use an integrated, numerical animation model as we do in Animated.spring, but instead solved for the closed form of the equations that govern damped harmonic oscillation (the differential equations themselves are [here](https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator), and a paper describing the math to arrive at the closed-form solution to the second-order ODE that describes the DHO is [here](http://planetmath.org/sites/default/files/texpdf/39745.pdf)). Though we can get the currently implemented RK4 integration close by tweaking some values, it is, the current model is at it's core, an approximation. It seemed that if I wanted to implement the `CASpringAnimation` behavior _exactly_, I needed to implement the analytical model (as is implemented in `CASpringAnimation`) in `Animated`. We add three new optional parameters to `Animated.spring` (to both the JS and native implementations): - `stiffness`, a value describing the spring's stiffness coefficient - `damping`, a value defining how the spring's motion should be damped due to the forces of friction (technically called the _viscous damping coefficient_). - `mass`, a value describing the mass of the object attached to the end of the simulated spring Just like if a developer were to specify `bounciness`/`speed` and `tension`/`friction` in the same config, specifying any of these new parameters while also specifying the aforementioned config values will cause an error to be thrown. ~Defaults for `Animated.spring` across all three implementations (JS/iOS/Android) stay the same, so this is intended to be *a non-breaking change*.~ ~If `stiffness`, `damping`, or `mass` are provided in the config, we switch to animating the spring with the new damped harmonic oscillator model (`DHO` as described in the code).~ We replace the old RK4 integration implementation with our new analytic implementation. Tension/friction nicely correspond directly to stiffness/damping with the mass of the spring locked at 1. This is intended to be *a non-breaking change*, but there may be very slight differences in people's springs (maybe not even noticeable to the naked eye), given the fact that this implementation is more accurate. The DHO animation algorithm will calculate the _position_ of the spring at time _t_ explicitly and in an analytical fashion, and use this calculation to update the animation's value. It will also analytically calculate the velocity at time _t_, so as to allow animated value tracking to continue to work as expected. Also, docs have been updated to cover the new configuration options (and also I added docs for Animated configuration options that were missing, such as `restDisplacementThreshold`, etc). Run tests. Run "Animated Gratuitous App" and "NativeAnimation" example in RNTester. Closes https://github.com/facebook/react-native/pull/15322 Differential Revision: D5794791 Pulled By: hramos fbshipit-source-id: 58ed9e134a097e321c85c417a142576f6a8952f8
2017-09-21 06:36:11 +00:00
[self onUpdate:position];
Add closed-form damped harmonic oscillator algorithm to Animated.spring Summary: As I was working on mimicking iOS animations for my ongoing work with `react-navigation`, one task I had was to match the "push from right" animation that is common in UINavigationController. I was able to grab the exact animation values for this animation with some LLDB magic, and found that the screen is animated using a `CASpringAnimation` with the parameters: - stiffness: 1000 - damping: 500 - mass: 3 After spending a considerable amount of time attempting to replicate the spring created with these values by CASpringAnimation by specifying values for tension and friction in the current `Animated.spring` implementation, I was unable to come up with mathematically equivalent values that could replicate the spring _exactly_. After doing some research, I ended up disassembling the QuartzCore framework, reading the assembly, and determined that Apple's implementation of `CASpringAnimation` does not use an integrated, numerical animation model as we do in Animated.spring, but instead solved for the closed form of the equations that govern damped harmonic oscillation (the differential equations themselves are [here](https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator), and a paper describing the math to arrive at the closed-form solution to the second-order ODE that describes the DHO is [here](http://planetmath.org/sites/default/files/texpdf/39745.pdf)). Though we can get the currently implemented RK4 integration close by tweaking some values, it is, the current model is at it's core, an approximation. It seemed that if I wanted to implement the `CASpringAnimation` behavior _exactly_, I needed to implement the analytical model (as is implemented in `CASpringAnimation`) in `Animated`. We add three new optional parameters to `Animated.spring` (to both the JS and native implementations): - `stiffness`, a value describing the spring's stiffness coefficient - `damping`, a value defining how the spring's motion should be damped due to the forces of friction (technically called the _viscous damping coefficient_). - `mass`, a value describing the mass of the object attached to the end of the simulated spring Just like if a developer were to specify `bounciness`/`speed` and `tension`/`friction` in the same config, specifying any of these new parameters while also specifying the aforementioned config values will cause an error to be thrown. ~Defaults for `Animated.spring` across all three implementations (JS/iOS/Android) stay the same, so this is intended to be *a non-breaking change*.~ ~If `stiffness`, `damping`, or `mass` are provided in the config, we switch to animating the spring with the new damped harmonic oscillator model (`DHO` as described in the code).~ We replace the old RK4 integration implementation with our new analytic implementation. Tension/friction nicely correspond directly to stiffness/damping with the mass of the spring locked at 1. This is intended to be *a non-breaking change*, but there may be very slight differences in people's springs (maybe not even noticeable to the naked eye), given the fact that this implementation is more accurate. The DHO animation algorithm will calculate the _position_ of the spring at time _t_ explicitly and in an analytical fashion, and use this calculation to update the animation's value. It will also analytically calculate the velocity at time _t_, so as to allow animated value tracking to continue to work as expected. Also, docs have been updated to cover the new configuration options (and also I added docs for Animated configuration options that were missing, such as `restDisplacementThreshold`, etc). Run tests. Run "Animated Gratuitous App" and "NativeAnimation" example in RNTester. Closes https://github.com/facebook/react-native/pull/15322 Differential Revision: D5794791 Pulled By: hramos fbshipit-source-id: 58ed9e134a097e321c85c417a142576f6a8952f8
2017-09-21 06:36:11 +00:00
// Conditions for stopping the spring animation
BOOL isOvershooting = NO;
Add closed-form damped harmonic oscillator algorithm to Animated.spring Summary: As I was working on mimicking iOS animations for my ongoing work with `react-navigation`, one task I had was to match the "push from right" animation that is common in UINavigationController. I was able to grab the exact animation values for this animation with some LLDB magic, and found that the screen is animated using a `CASpringAnimation` with the parameters: - stiffness: 1000 - damping: 500 - mass: 3 After spending a considerable amount of time attempting to replicate the spring created with these values by CASpringAnimation by specifying values for tension and friction in the current `Animated.spring` implementation, I was unable to come up with mathematically equivalent values that could replicate the spring _exactly_. After doing some research, I ended up disassembling the QuartzCore framework, reading the assembly, and determined that Apple's implementation of `CASpringAnimation` does not use an integrated, numerical animation model as we do in Animated.spring, but instead solved for the closed form of the equations that govern damped harmonic oscillation (the differential equations themselves are [here](https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator), and a paper describing the math to arrive at the closed-form solution to the second-order ODE that describes the DHO is [here](http://planetmath.org/sites/default/files/texpdf/39745.pdf)). Though we can get the currently implemented RK4 integration close by tweaking some values, it is, the current model is at it's core, an approximation. It seemed that if I wanted to implement the `CASpringAnimation` behavior _exactly_, I needed to implement the analytical model (as is implemented in `CASpringAnimation`) in `Animated`. We add three new optional parameters to `Animated.spring` (to both the JS and native implementations): - `stiffness`, a value describing the spring's stiffness coefficient - `damping`, a value defining how the spring's motion should be damped due to the forces of friction (technically called the _viscous damping coefficient_). - `mass`, a value describing the mass of the object attached to the end of the simulated spring Just like if a developer were to specify `bounciness`/`speed` and `tension`/`friction` in the same config, specifying any of these new parameters while also specifying the aforementioned config values will cause an error to be thrown. ~Defaults for `Animated.spring` across all three implementations (JS/iOS/Android) stay the same, so this is intended to be *a non-breaking change*.~ ~If `stiffness`, `damping`, or `mass` are provided in the config, we switch to animating the spring with the new damped harmonic oscillator model (`DHO` as described in the code).~ We replace the old RK4 integration implementation with our new analytic implementation. Tension/friction nicely correspond directly to stiffness/damping with the mass of the spring locked at 1. This is intended to be *a non-breaking change*, but there may be very slight differences in people's springs (maybe not even noticeable to the naked eye), given the fact that this implementation is more accurate. The DHO animation algorithm will calculate the _position_ of the spring at time _t_ explicitly and in an analytical fashion, and use this calculation to update the animation's value. It will also analytically calculate the velocity at time _t_, so as to allow animated value tracking to continue to work as expected. Also, docs have been updated to cover the new configuration options (and also I added docs for Animated configuration options that were missing, such as `restDisplacementThreshold`, etc). Run tests. Run "Animated Gratuitous App" and "NativeAnimation" example in RNTester. Closes https://github.com/facebook/react-native/pull/15322 Differential Revision: D5794791 Pulled By: hramos fbshipit-source-id: 58ed9e134a097e321c85c417a142576f6a8952f8
2017-09-21 06:36:11 +00:00
if (_overshootClamping && _stiffness != 0) {
if (_fromValue < _toValue) {
isOvershooting = position > _toValue;
} else {
isOvershooting = position < _toValue;
}
}
BOOL isVelocity = ABS(velocity) <= _restSpeedThreshold;
BOOL isDisplacement = YES;
Add closed-form damped harmonic oscillator algorithm to Animated.spring Summary: As I was working on mimicking iOS animations for my ongoing work with `react-navigation`, one task I had was to match the "push from right" animation that is common in UINavigationController. I was able to grab the exact animation values for this animation with some LLDB magic, and found that the screen is animated using a `CASpringAnimation` with the parameters: - stiffness: 1000 - damping: 500 - mass: 3 After spending a considerable amount of time attempting to replicate the spring created with these values by CASpringAnimation by specifying values for tension and friction in the current `Animated.spring` implementation, I was unable to come up with mathematically equivalent values that could replicate the spring _exactly_. After doing some research, I ended up disassembling the QuartzCore framework, reading the assembly, and determined that Apple's implementation of `CASpringAnimation` does not use an integrated, numerical animation model as we do in Animated.spring, but instead solved for the closed form of the equations that govern damped harmonic oscillation (the differential equations themselves are [here](https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator), and a paper describing the math to arrive at the closed-form solution to the second-order ODE that describes the DHO is [here](http://planetmath.org/sites/default/files/texpdf/39745.pdf)). Though we can get the currently implemented RK4 integration close by tweaking some values, it is, the current model is at it's core, an approximation. It seemed that if I wanted to implement the `CASpringAnimation` behavior _exactly_, I needed to implement the analytical model (as is implemented in `CASpringAnimation`) in `Animated`. We add three new optional parameters to `Animated.spring` (to both the JS and native implementations): - `stiffness`, a value describing the spring's stiffness coefficient - `damping`, a value defining how the spring's motion should be damped due to the forces of friction (technically called the _viscous damping coefficient_). - `mass`, a value describing the mass of the object attached to the end of the simulated spring Just like if a developer were to specify `bounciness`/`speed` and `tension`/`friction` in the same config, specifying any of these new parameters while also specifying the aforementioned config values will cause an error to be thrown. ~Defaults for `Animated.spring` across all three implementations (JS/iOS/Android) stay the same, so this is intended to be *a non-breaking change*.~ ~If `stiffness`, `damping`, or `mass` are provided in the config, we switch to animating the spring with the new damped harmonic oscillator model (`DHO` as described in the code).~ We replace the old RK4 integration implementation with our new analytic implementation. Tension/friction nicely correspond directly to stiffness/damping with the mass of the spring locked at 1. This is intended to be *a non-breaking change*, but there may be very slight differences in people's springs (maybe not even noticeable to the naked eye), given the fact that this implementation is more accurate. The DHO animation algorithm will calculate the _position_ of the spring at time _t_ explicitly and in an analytical fashion, and use this calculation to update the animation's value. It will also analytically calculate the velocity at time _t_, so as to allow animated value tracking to continue to work as expected. Also, docs have been updated to cover the new configuration options (and also I added docs for Animated configuration options that were missing, such as `restDisplacementThreshold`, etc). Run tests. Run "Animated Gratuitous App" and "NativeAnimation" example in RNTester. Closes https://github.com/facebook/react-native/pull/15322 Differential Revision: D5794791 Pulled By: hramos fbshipit-source-id: 58ed9e134a097e321c85c417a142576f6a8952f8
2017-09-21 06:36:11 +00:00
if (_stiffness != 0) {
isDisplacement = ABS(_toValue - position) <= _restDisplacementThreshold;
}
Add closed-form damped harmonic oscillator algorithm to Animated.spring Summary: As I was working on mimicking iOS animations for my ongoing work with `react-navigation`, one task I had was to match the "push from right" animation that is common in UINavigationController. I was able to grab the exact animation values for this animation with some LLDB magic, and found that the screen is animated using a `CASpringAnimation` with the parameters: - stiffness: 1000 - damping: 500 - mass: 3 After spending a considerable amount of time attempting to replicate the spring created with these values by CASpringAnimation by specifying values for tension and friction in the current `Animated.spring` implementation, I was unable to come up with mathematically equivalent values that could replicate the spring _exactly_. After doing some research, I ended up disassembling the QuartzCore framework, reading the assembly, and determined that Apple's implementation of `CASpringAnimation` does not use an integrated, numerical animation model as we do in Animated.spring, but instead solved for the closed form of the equations that govern damped harmonic oscillation (the differential equations themselves are [here](https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator), and a paper describing the math to arrive at the closed-form solution to the second-order ODE that describes the DHO is [here](http://planetmath.org/sites/default/files/texpdf/39745.pdf)). Though we can get the currently implemented RK4 integration close by tweaking some values, it is, the current model is at it's core, an approximation. It seemed that if I wanted to implement the `CASpringAnimation` behavior _exactly_, I needed to implement the analytical model (as is implemented in `CASpringAnimation`) in `Animated`. We add three new optional parameters to `Animated.spring` (to both the JS and native implementations): - `stiffness`, a value describing the spring's stiffness coefficient - `damping`, a value defining how the spring's motion should be damped due to the forces of friction (technically called the _viscous damping coefficient_). - `mass`, a value describing the mass of the object attached to the end of the simulated spring Just like if a developer were to specify `bounciness`/`speed` and `tension`/`friction` in the same config, specifying any of these new parameters while also specifying the aforementioned config values will cause an error to be thrown. ~Defaults for `Animated.spring` across all three implementations (JS/iOS/Android) stay the same, so this is intended to be *a non-breaking change*.~ ~If `stiffness`, `damping`, or `mass` are provided in the config, we switch to animating the spring with the new damped harmonic oscillator model (`DHO` as described in the code).~ We replace the old RK4 integration implementation with our new analytic implementation. Tension/friction nicely correspond directly to stiffness/damping with the mass of the spring locked at 1. This is intended to be *a non-breaking change*, but there may be very slight differences in people's springs (maybe not even noticeable to the naked eye), given the fact that this implementation is more accurate. The DHO animation algorithm will calculate the _position_ of the spring at time _t_ explicitly and in an analytical fashion, and use this calculation to update the animation's value. It will also analytically calculate the velocity at time _t_, so as to allow animated value tracking to continue to work as expected. Also, docs have been updated to cover the new configuration options (and also I added docs for Animated configuration options that were missing, such as `restDisplacementThreshold`, etc). Run tests. Run "Animated Gratuitous App" and "NativeAnimation" example in RNTester. Closes https://github.com/facebook/react-native/pull/15322 Differential Revision: D5794791 Pulled By: hramos fbshipit-source-id: 58ed9e134a097e321c85c417a142576f6a8952f8
2017-09-21 06:36:11 +00:00
if (isOvershooting || (isVelocity && isDisplacement)) {
Add closed-form damped harmonic oscillator algorithm to Animated.spring Summary: As I was working on mimicking iOS animations for my ongoing work with `react-navigation`, one task I had was to match the "push from right" animation that is common in UINavigationController. I was able to grab the exact animation values for this animation with some LLDB magic, and found that the screen is animated using a `CASpringAnimation` with the parameters: - stiffness: 1000 - damping: 500 - mass: 3 After spending a considerable amount of time attempting to replicate the spring created with these values by CASpringAnimation by specifying values for tension and friction in the current `Animated.spring` implementation, I was unable to come up with mathematically equivalent values that could replicate the spring _exactly_. After doing some research, I ended up disassembling the QuartzCore framework, reading the assembly, and determined that Apple's implementation of `CASpringAnimation` does not use an integrated, numerical animation model as we do in Animated.spring, but instead solved for the closed form of the equations that govern damped harmonic oscillation (the differential equations themselves are [here](https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator), and a paper describing the math to arrive at the closed-form solution to the second-order ODE that describes the DHO is [here](http://planetmath.org/sites/default/files/texpdf/39745.pdf)). Though we can get the currently implemented RK4 integration close by tweaking some values, it is, the current model is at it's core, an approximation. It seemed that if I wanted to implement the `CASpringAnimation` behavior _exactly_, I needed to implement the analytical model (as is implemented in `CASpringAnimation`) in `Animated`. We add three new optional parameters to `Animated.spring` (to both the JS and native implementations): - `stiffness`, a value describing the spring's stiffness coefficient - `damping`, a value defining how the spring's motion should be damped due to the forces of friction (technically called the _viscous damping coefficient_). - `mass`, a value describing the mass of the object attached to the end of the simulated spring Just like if a developer were to specify `bounciness`/`speed` and `tension`/`friction` in the same config, specifying any of these new parameters while also specifying the aforementioned config values will cause an error to be thrown. ~Defaults for `Animated.spring` across all three implementations (JS/iOS/Android) stay the same, so this is intended to be *a non-breaking change*.~ ~If `stiffness`, `damping`, or `mass` are provided in the config, we switch to animating the spring with the new damped harmonic oscillator model (`DHO` as described in the code).~ We replace the old RK4 integration implementation with our new analytic implementation. Tension/friction nicely correspond directly to stiffness/damping with the mass of the spring locked at 1. This is intended to be *a non-breaking change*, but there may be very slight differences in people's springs (maybe not even noticeable to the naked eye), given the fact that this implementation is more accurate. The DHO animation algorithm will calculate the _position_ of the spring at time _t_ explicitly and in an analytical fashion, and use this calculation to update the animation's value. It will also analytically calculate the velocity at time _t_, so as to allow animated value tracking to continue to work as expected. Also, docs have been updated to cover the new configuration options (and also I added docs for Animated configuration options that were missing, such as `restDisplacementThreshold`, etc). Run tests. Run "Animated Gratuitous App" and "NativeAnimation" example in RNTester. Closes https://github.com/facebook/react-native/pull/15322 Differential Revision: D5794791 Pulled By: hramos fbshipit-source-id: 58ed9e134a097e321c85c417a142576f6a8952f8
2017-09-21 06:36:11 +00:00
if (_stiffness != 0) {
// Ensure that we end up with a round value
if (_animationHasFinished) {
return;
}
[self onUpdate:_toValue];
}
Add closed-form damped harmonic oscillator algorithm to Animated.spring Summary: As I was working on mimicking iOS animations for my ongoing work with `react-navigation`, one task I had was to match the "push from right" animation that is common in UINavigationController. I was able to grab the exact animation values for this animation with some LLDB magic, and found that the screen is animated using a `CASpringAnimation` with the parameters: - stiffness: 1000 - damping: 500 - mass: 3 After spending a considerable amount of time attempting to replicate the spring created with these values by CASpringAnimation by specifying values for tension and friction in the current `Animated.spring` implementation, I was unable to come up with mathematically equivalent values that could replicate the spring _exactly_. After doing some research, I ended up disassembling the QuartzCore framework, reading the assembly, and determined that Apple's implementation of `CASpringAnimation` does not use an integrated, numerical animation model as we do in Animated.spring, but instead solved for the closed form of the equations that govern damped harmonic oscillation (the differential equations themselves are [here](https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator), and a paper describing the math to arrive at the closed-form solution to the second-order ODE that describes the DHO is [here](http://planetmath.org/sites/default/files/texpdf/39745.pdf)). Though we can get the currently implemented RK4 integration close by tweaking some values, it is, the current model is at it's core, an approximation. It seemed that if I wanted to implement the `CASpringAnimation` behavior _exactly_, I needed to implement the analytical model (as is implemented in `CASpringAnimation`) in `Animated`. We add three new optional parameters to `Animated.spring` (to both the JS and native implementations): - `stiffness`, a value describing the spring's stiffness coefficient - `damping`, a value defining how the spring's motion should be damped due to the forces of friction (technically called the _viscous damping coefficient_). - `mass`, a value describing the mass of the object attached to the end of the simulated spring Just like if a developer were to specify `bounciness`/`speed` and `tension`/`friction` in the same config, specifying any of these new parameters while also specifying the aforementioned config values will cause an error to be thrown. ~Defaults for `Animated.spring` across all three implementations (JS/iOS/Android) stay the same, so this is intended to be *a non-breaking change*.~ ~If `stiffness`, `damping`, or `mass` are provided in the config, we switch to animating the spring with the new damped harmonic oscillator model (`DHO` as described in the code).~ We replace the old RK4 integration implementation with our new analytic implementation. Tension/friction nicely correspond directly to stiffness/damping with the mass of the spring locked at 1. This is intended to be *a non-breaking change*, but there may be very slight differences in people's springs (maybe not even noticeable to the naked eye), given the fact that this implementation is more accurate. The DHO animation algorithm will calculate the _position_ of the spring at time _t_ explicitly and in an analytical fashion, and use this calculation to update the animation's value. It will also analytically calculate the velocity at time _t_, so as to allow animated value tracking to continue to work as expected. Also, docs have been updated to cover the new configuration options (and also I added docs for Animated configuration options that were missing, such as `restDisplacementThreshold`, etc). Run tests. Run "Animated Gratuitous App" and "NativeAnimation" example in RNTester. Closes https://github.com/facebook/react-native/pull/15322 Differential Revision: D5794791 Pulled By: hramos fbshipit-source-id: 58ed9e134a097e321c85c417a142576f6a8952f8
2017-09-21 06:36:11 +00:00
if (_iterations == -1 || _currentLoop < _iterations) {
_lastPosition = _fromValue;
_lastVelocity = _initialVelocity;
Add closed-form damped harmonic oscillator algorithm to Animated.spring Summary: As I was working on mimicking iOS animations for my ongoing work with `react-navigation`, one task I had was to match the "push from right" animation that is common in UINavigationController. I was able to grab the exact animation values for this animation with some LLDB magic, and found that the screen is animated using a `CASpringAnimation` with the parameters: - stiffness: 1000 - damping: 500 - mass: 3 After spending a considerable amount of time attempting to replicate the spring created with these values by CASpringAnimation by specifying values for tension and friction in the current `Animated.spring` implementation, I was unable to come up with mathematically equivalent values that could replicate the spring _exactly_. After doing some research, I ended up disassembling the QuartzCore framework, reading the assembly, and determined that Apple's implementation of `CASpringAnimation` does not use an integrated, numerical animation model as we do in Animated.spring, but instead solved for the closed form of the equations that govern damped harmonic oscillation (the differential equations themselves are [here](https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator), and a paper describing the math to arrive at the closed-form solution to the second-order ODE that describes the DHO is [here](http://planetmath.org/sites/default/files/texpdf/39745.pdf)). Though we can get the currently implemented RK4 integration close by tweaking some values, it is, the current model is at it's core, an approximation. It seemed that if I wanted to implement the `CASpringAnimation` behavior _exactly_, I needed to implement the analytical model (as is implemented in `CASpringAnimation`) in `Animated`. We add three new optional parameters to `Animated.spring` (to both the JS and native implementations): - `stiffness`, a value describing the spring's stiffness coefficient - `damping`, a value defining how the spring's motion should be damped due to the forces of friction (technically called the _viscous damping coefficient_). - `mass`, a value describing the mass of the object attached to the end of the simulated spring Just like if a developer were to specify `bounciness`/`speed` and `tension`/`friction` in the same config, specifying any of these new parameters while also specifying the aforementioned config values will cause an error to be thrown. ~Defaults for `Animated.spring` across all three implementations (JS/iOS/Android) stay the same, so this is intended to be *a non-breaking change*.~ ~If `stiffness`, `damping`, or `mass` are provided in the config, we switch to animating the spring with the new damped harmonic oscillator model (`DHO` as described in the code).~ We replace the old RK4 integration implementation with our new analytic implementation. Tension/friction nicely correspond directly to stiffness/damping with the mass of the spring locked at 1. This is intended to be *a non-breaking change*, but there may be very slight differences in people's springs (maybe not even noticeable to the naked eye), given the fact that this implementation is more accurate. The DHO animation algorithm will calculate the _position_ of the spring at time _t_ explicitly and in an analytical fashion, and use this calculation to update the animation's value. It will also analytically calculate the velocity at time _t_, so as to allow animated value tracking to continue to work as expected. Also, docs have been updated to cover the new configuration options (and also I added docs for Animated configuration options that were missing, such as `restDisplacementThreshold`, etc). Run tests. Run "Animated Gratuitous App" and "NativeAnimation" example in RNTester. Closes https://github.com/facebook/react-native/pull/15322 Differential Revision: D5794791 Pulled By: hramos fbshipit-source-id: 58ed9e134a097e321c85c417a142576f6a8952f8
2017-09-21 06:36:11 +00:00
// Set _animationStartTime to -1 to reset instance variables on the next animation step.
_animationStartTime = -1;
_currentLoop++;
[self onUpdate:_fromValue];
} else {
_animationHasFinished = YES;
}
}
}
- (void)onUpdate:(CGFloat)outputValue
{
_valueNode.value = outputValue;
[_valueNode setNeedsUpdate];
}
@end