RCTDecayAnimation.m 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  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/RCTDecayAnimation.h>
  8. #import <UIKit/UIKit.h>
  9. #import <React/RCTConvert.h>
  10. #import <React/RCTAnimationUtils.h>
  11. #import <React/RCTValueAnimatedNode.h>
  12. @interface RCTDecayAnimation ()
  13. @property (nonatomic, strong) NSNumber *animationId;
  14. @property (nonatomic, strong) RCTValueAnimatedNode *valueNode;
  15. @property (nonatomic, assign) BOOL animationHasBegun;
  16. @property (nonatomic, assign) BOOL animationHasFinished;
  17. @end
  18. @implementation RCTDecayAnimation
  19. {
  20. CGFloat _velocity;
  21. CGFloat _deceleration;
  22. NSTimeInterval _frameStartTime;
  23. CGFloat _fromValue;
  24. CGFloat _lastValue;
  25. NSInteger _iterations;
  26. NSInteger _currentLoop;
  27. RCTResponseSenderBlock _callback;
  28. }
  29. - (instancetype)initWithId:(NSNumber *)animationId
  30. config:(NSDictionary *)config
  31. forNode:(RCTValueAnimatedNode *)valueNode
  32. callBack:(nullable RCTResponseSenderBlock)callback
  33. {
  34. if ((self = [super init])) {
  35. _callback = [callback copy];
  36. _animationId = animationId;
  37. _valueNode = valueNode;
  38. _fromValue = 0;
  39. _lastValue = 0;
  40. _velocity = [RCTConvert CGFloat:config[@"velocity"]]; // initial velocity
  41. [self resetAnimationConfig:config];
  42. }
  43. return self;
  44. }
  45. - (void)resetAnimationConfig:(NSDictionary *)config
  46. {
  47. NSNumber *iterations = [RCTConvert NSNumber:config[@"iterations"]] ?: @1;
  48. _fromValue = _lastValue;
  49. _deceleration = [RCTConvert CGFloat:config[@"deceleration"]];
  50. _iterations = iterations.integerValue;
  51. _currentLoop = 1;
  52. _animationHasFinished = iterations.integerValue == 0;
  53. }
  54. RCT_NOT_IMPLEMENTED(- (instancetype)init)
  55. - (void)startAnimation
  56. {
  57. _frameStartTime = -1;
  58. _animationHasBegun = YES;
  59. }
  60. - (void)stopAnimation
  61. {
  62. _valueNode = nil;
  63. if (_callback) {
  64. _callback(@[@{
  65. @"finished": @(_animationHasFinished)
  66. }]);
  67. }
  68. }
  69. - (void)stepAnimationWithTime:(NSTimeInterval)currentTime
  70. {
  71. if (!_animationHasBegun || _animationHasFinished) {
  72. // Animation has not begun or animation has already finished.
  73. return;
  74. }
  75. if (_frameStartTime == -1) {
  76. // Since this is the first animation step, consider the start to be on the previous frame.
  77. _frameStartTime = currentTime - RCTSingleFrameInterval;
  78. if (_fromValue == _lastValue) {
  79. // First iteration, assign _fromValue based on _valueNode.
  80. _fromValue = _valueNode.value;
  81. } else {
  82. // Not the first iteration, reset _valueNode based on _fromValue.
  83. [self updateValue:_fromValue];
  84. }
  85. _lastValue = _valueNode.value;
  86. }
  87. CGFloat value = _fromValue +
  88. (_velocity / (1 - _deceleration)) *
  89. (1 - exp(-(1 - _deceleration) * (currentTime - _frameStartTime) * 1000.0 / RCTAnimationDragCoefficient()));
  90. [self updateValue:value];
  91. if (fabs(_lastValue - value) < 0.1) {
  92. if (_iterations == -1 || _currentLoop < _iterations) {
  93. // Set _frameStartTime to -1 to reset instance variables on the next runAnimationStep.
  94. _frameStartTime = -1;
  95. _currentLoop++;
  96. } else {
  97. _animationHasFinished = true;
  98. return;
  99. }
  100. }
  101. _lastValue = value;
  102. }
  103. - (void)updateValue:(CGFloat)outputValue
  104. {
  105. _valueNode.value = outputValue;
  106. [_valueNode setNeedsUpdate];
  107. }
  108. @end