RCTSpringAnimation.m 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. #import <React/RCTSpringAnimation.h>
  8. #import <UIKit/UIKit.h>
  9. #import <React/RCTConvert.h>
  10. #import <React/RCTDefines.h>
  11. #import <React/RCTAnimationUtils.h>
  12. #import <React/RCTValueAnimatedNode.h>
  13. @interface RCTSpringAnimation ()
  14. @property (nonatomic, strong) NSNumber *animationId;
  15. @property (nonatomic, strong) RCTValueAnimatedNode *valueNode;
  16. @property (nonatomic, assign) BOOL animationHasBegun;
  17. @property (nonatomic, assign) BOOL animationHasFinished;
  18. @end
  19. const NSTimeInterval MAX_DELTA_TIME = 0.064;
  20. @implementation RCTSpringAnimation
  21. {
  22. CGFloat _toValue;
  23. CGFloat _fromValue;
  24. BOOL _overshootClamping;
  25. CGFloat _restDisplacementThreshold;
  26. CGFloat _restSpeedThreshold;
  27. CGFloat _stiffness;
  28. CGFloat _damping;
  29. CGFloat _mass;
  30. CGFloat _initialVelocity;
  31. NSTimeInterval _animationStartTime;
  32. NSTimeInterval _animationCurrentTime;
  33. RCTResponseSenderBlock _callback;
  34. CGFloat _lastPosition;
  35. CGFloat _lastVelocity;
  36. NSInteger _iterations;
  37. NSInteger _currentLoop;
  38. NSTimeInterval _t; // Current time (startTime + dt)
  39. }
  40. - (instancetype)initWithId:(NSNumber *)animationId
  41. config:(NSDictionary *)config
  42. forNode:(RCTValueAnimatedNode *)valueNode
  43. callBack:(nullable RCTResponseSenderBlock)callback
  44. {
  45. if ((self = [super init])) {
  46. _animationId = animationId;
  47. _lastPosition = valueNode.value;
  48. _valueNode = valueNode;
  49. _lastVelocity = [RCTConvert CGFloat:config[@"initialVelocity"]];
  50. _callback = [callback copy];
  51. [self resetAnimationConfig:config];
  52. }
  53. return self;
  54. }
  55. - (void)resetAnimationConfig:(NSDictionary *)config
  56. {
  57. NSNumber *iterations = [RCTConvert NSNumber:config[@"iterations"]] ?: @1;
  58. _toValue = [RCTConvert CGFloat:config[@"toValue"]];
  59. _overshootClamping = [RCTConvert BOOL:config[@"overshootClamping"]];
  60. _restDisplacementThreshold = [RCTConvert CGFloat:config[@"restDisplacementThreshold"]];
  61. _restSpeedThreshold = [RCTConvert CGFloat:config[@"restSpeedThreshold"]];
  62. _stiffness = [RCTConvert CGFloat:config[@"stiffness"]];
  63. _damping = [RCTConvert CGFloat:config[@"damping"]];
  64. _mass = [RCTConvert CGFloat:config[@"mass"]];
  65. _initialVelocity = _lastVelocity;
  66. _fromValue = _lastPosition;
  67. _fromValue = _lastPosition;
  68. _lastVelocity = _initialVelocity;
  69. _animationHasFinished = iterations.integerValue == 0;
  70. _iterations = iterations.integerValue;
  71. _currentLoop = 1;
  72. _animationStartTime = _animationCurrentTime = -1;
  73. _animationHasBegun = YES;
  74. }
  75. RCT_NOT_IMPLEMENTED(- (instancetype)init)
  76. - (void)startAnimation
  77. {
  78. _animationStartTime = _animationCurrentTime = -1;
  79. _animationHasBegun = YES;
  80. }
  81. - (void)stopAnimation
  82. {
  83. _valueNode = nil;
  84. if (_callback) {
  85. _callback(@[@{
  86. @"finished": @(_animationHasFinished)
  87. }]);
  88. }
  89. }
  90. - (void)stepAnimationWithTime:(NSTimeInterval)currentTime
  91. {
  92. if (!_animationHasBegun || _animationHasFinished) {
  93. // Animation has not begun or animation has already finished.
  94. return;
  95. }
  96. // calculate delta time
  97. if(_animationStartTime == -1) {
  98. _t = 0.0;
  99. _animationStartTime = currentTime;
  100. } else {
  101. // Handle frame drops, and only advance dt by a max of MAX_DELTA_TIME
  102. NSTimeInterval deltaTime = MIN(MAX_DELTA_TIME, currentTime - _animationCurrentTime);
  103. _t = _t + deltaTime / RCTAnimationDragCoefficient();
  104. }
  105. // store the timestamp
  106. _animationCurrentTime = currentTime;
  107. CGFloat c = _damping;
  108. CGFloat m = _mass;
  109. CGFloat k = _stiffness;
  110. CGFloat v0 = -_initialVelocity;
  111. CGFloat zeta = c / (2 * sqrtf(k * m));
  112. CGFloat omega0 = sqrtf(k / m);
  113. CGFloat omega1 = omega0 * sqrtf(1.0 - (zeta * zeta));
  114. CGFloat x0 = _toValue - _fromValue;
  115. CGFloat position;
  116. CGFloat velocity;
  117. if (zeta < 1) {
  118. // Under damped
  119. CGFloat envelope = expf(-zeta * omega0 * _t);
  120. position =
  121. _toValue -
  122. envelope *
  123. ((v0 + zeta * omega0 * x0) / omega1 * sinf(omega1 * _t) +
  124. x0 * cosf(omega1 * _t));
  125. // This looks crazy -- it's actually just the derivative of the
  126. // oscillation function
  127. velocity =
  128. zeta *
  129. omega0 *
  130. envelope *
  131. (sinf(omega1 * _t) * (v0 + zeta * omega0 * x0) / omega1 +
  132. x0 * cosf(omega1 * _t)) -
  133. envelope *
  134. (cosf(omega1 * _t) * (v0 + zeta * omega0 * x0) -
  135. omega1 * x0 * sinf(omega1 * _t));
  136. } else {
  137. CGFloat envelope = expf(-omega0 * _t);
  138. position = _toValue - envelope * (x0 + (v0 + omega0 * x0) * _t);
  139. velocity =
  140. envelope * (v0 * (_t * omega0 - 1) + _t * x0 * (omega0 * omega0));
  141. }
  142. _lastPosition = position;
  143. _lastVelocity = velocity;
  144. [self onUpdate:position];
  145. // Conditions for stopping the spring animation
  146. BOOL isOvershooting = NO;
  147. if (_overshootClamping && _stiffness != 0) {
  148. if (_fromValue < _toValue) {
  149. isOvershooting = position > _toValue;
  150. } else {
  151. isOvershooting = position < _toValue;
  152. }
  153. }
  154. BOOL isVelocity = ABS(velocity) <= _restSpeedThreshold;
  155. BOOL isDisplacement = YES;
  156. if (_stiffness != 0) {
  157. isDisplacement = ABS(_toValue - position) <= _restDisplacementThreshold;
  158. }
  159. if (isOvershooting || (isVelocity && isDisplacement)) {
  160. if (_stiffness != 0) {
  161. // Ensure that we end up with a round value
  162. if (_animationHasFinished) {
  163. return;
  164. }
  165. [self onUpdate:_toValue];
  166. }
  167. if (_iterations == -1 || _currentLoop < _iterations) {
  168. _lastPosition = _fromValue;
  169. _lastVelocity = _initialVelocity;
  170. // Set _animationStartTime to -1 to reset instance variables on the next animation step.
  171. _animationStartTime = -1;
  172. _currentLoop++;
  173. [self onUpdate:_fromValue];
  174. } else {
  175. _animationHasFinished = YES;
  176. }
  177. }
  178. }
  179. - (void)onUpdate:(CGFloat)outputValue
  180. {
  181. _valueNode.value = outputValue;
  182. [_valueNode setNeedsUpdate];
  183. }
  184. @end