Add support for springs for NativeAnimated on iOS

Summary:
This diff adds support for native spring animations on iOS. This overlaps some spring work done by kmagiera on the Android side of things.

**Test plan (required)**

Run UIExplorer NativeAnimated examples before and after - compare the results. Pay special attention to the spring examples.
Closes https://github.com/facebook/react-native/pull/9048

Differential Revision: D4056088

Pulled By: foghina

fbshipit-source-id: a593408cb61cb850572bab4a0884f7157cece656
This commit is contained in:
Ryan Gomba 2016-10-21 04:20:01 -07:00 committed by Facebook Github Bot
parent 5e94114497
commit d950db4ef7
7 changed files with 310 additions and 50 deletions

View File

@ -14,20 +14,17 @@
NS_ASSUME_NONNULL_BEGIN
@interface RCTAnimationDriverNode : NSObject
@protocol RCTAnimationDriver <NSObject>
@property (nonatomic, readonly) NSNumber *animationId;
@property (nonatomic, readonly) NSNumber *outputValue;
@property (nonatomic, readonly) RCTValueAnimatedNode *valueNode;
@property (nonatomic, readonly) BOOL animationHasBegun;
@property (nonatomic, readonly) BOOL animationHasFinished;
- (instancetype)initWithId:(NSNumber *)animationId
delay:(NSTimeInterval)delay
toValue:(CGFloat)toValue
frames:(NSArray<NSNumber *> *)frames
config:(NSDictionary *)config
forNode:(RCTValueAnimatedNode *)valueNode
callBack:(nullable RCTResponseSenderBlock)callback NS_DESIGNATED_INITIALIZER;
callBack:(nullable RCTResponseSenderBlock)callback;
- (void)startAnimation;
- (void)stopAnimation;

View File

@ -0,0 +1,14 @@
/**
* 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 "RCTAnimationDriver.h"
@interface RCTFrameAnimation : NSObject<RCTAnimationDriver>
@end

View File

@ -7,17 +7,27 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTAnimationDriverNode.h"
#import "RCTFrameAnimation.h"
#import <UIKit/UIKit.h>
#import "RCTConvert.h"
#import "RCTAnimationUtils.h"
#import "RCTDefines.h"
#import "RCTValueAnimatedNode.h"
const double SINGLE_FRAME_INTERVAL = 1.0 / 60.0;
@implementation RCTAnimationDriverNode
@interface RCTFrameAnimation ()
@property (nonatomic, strong) NSNumber *animationId;
@property (nonatomic, strong) RCTValueAnimatedNode *valueNode;
@property (nonatomic, assign) BOOL animationHasBegun;
@property (nonatomic, assign) BOOL animationHasFinished;
@end
@implementation RCTFrameAnimation
{
NSArray<NSNumber *> *_frames;
CGFloat _toValue;
@ -25,25 +35,25 @@ const double SINGLE_FRAME_INTERVAL = 1.0 / 60.0;
NSTimeInterval _delay;
NSTimeInterval _animationStartTime;
NSTimeInterval _animationCurrentTime;
RCTValueAnimatedNode *_valueNode;
RCTResponseSenderBlock _callback;
}
- (instancetype)initWithId:(nonnull NSNumber *)animationId
delay:(NSTimeInterval)delay
toValue:(CGFloat)toValue
frames:(nonnull NSArray<NSNumber *> *)frames
forNode:(nonnull RCTValueAnimatedNode *)valueNode
callBack:(nullable RCTResponseSenderBlock)callback
- (instancetype)initWithId:(NSNumber *)animationId
config:(NSDictionary *)config
forNode:(RCTValueAnimatedNode *)valueNode
callBack:(nullable RCTResponseSenderBlock)callback;
{
if ((self = [super init])) {
NSNumber *toValue = [RCTConvert NSNumber:config[@"toValue"]] ?: @1;
NSTimeInterval delay = [RCTConvert double:config[@"delay"]];
NSArray<NSNumber *> *frames = [RCTConvert NSNumberArray:config[@"frames"]];
_animationId = animationId;
_toValue = toValue;
_toValue = toValue.floatValue;
_fromValue = valueNode.value;
_valueNode = valueNode;
_delay = delay;
_frames = [frames copy];
_outputValue = @0;
_callback = [callback copy];
}
return self;
@ -135,7 +145,6 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
EXTRAPOLATE_TYPE_EXTEND,
EXTRAPOLATE_TYPE_EXTEND);
_outputValue = @(outputValue);
_valueNode.value = outputValue;
[_valueNode setNeedsUpdate];
}

View File

@ -0,0 +1,14 @@
/**
* 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 "RCTAnimationDriver.h"
@interface RCTSpringAnimation : NSObject<RCTAnimationDriver>
@end

View File

@ -0,0 +1,200 @@
/**
* 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 "RCTConvert.h"
#import "RCTAnimationUtils.h"
#import "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
@implementation RCTSpringAnimation
{
CGFloat _toValue;
CGFloat _fromValue;
BOOL _overshootClamping;
CGFloat _restDisplacementThreshold;
CGFloat _restSpeedThreshold;
CGFloat _tension;
CGFloat _friction;
CGFloat _initialVelocity;
NSTimeInterval _animationStartTime;
NSTimeInterval _animationCurrentTime;
RCTResponseSenderBlock _callback;
CGFloat _lastPosition;
CGFloat _lastVelocity;
}
- (instancetype)initWithId:(NSNumber *)animationId
config:(NSDictionary *)config
forNode:(RCTValueAnimatedNode *)valueNode
callBack:(nullable RCTResponseSenderBlock)callback
{
if ((self = [super init])) {
_animationId = animationId;
_toValue = [RCTConvert CGFloat:config[@"toValue"]];
_fromValue = valueNode.value;
_valueNode = valueNode;
_overshootClamping = [RCTConvert BOOL:config[@"overshootClamping"]];
_restDisplacementThreshold = [RCTConvert CGFloat:config[@"restDisplacementThreshold"]];
_restSpeedThreshold = [RCTConvert CGFloat:config[@"restSpeedThreshold"]];
_tension = [RCTConvert CGFloat:config[@"tension"]];
_friction = [RCTConvert CGFloat:config[@"friction"]];
_initialVelocity = [RCTConvert CGFloat:config[@"initialVelocity"]];
_callback = [callback copy];
_lastPosition = _fromValue;
_lastVelocity = _initialVelocity;
}
return self;
}
RCT_NOT_IMPLEMENTED(- (instancetype)init)
- (void)startAnimation
{
_animationStartTime = CACurrentMediaTime();
_animationCurrentTime = _animationStartTime;
_animationHasBegun = YES;
}
- (void)stopAnimation
{
_animationHasFinished = YES;
}
- (void)removeAnimation
{
[self stopAnimation];
_valueNode = nil;
if (_callback) {
_callback(@[@{
@"finished": @(_animationHasFinished)
}]);
}
}
- (void)stepAnimation
{
if (!_animationHasBegun || _animationHasFinished) {
// Animation has not begun or animation has already finished.
return;
}
// We are using a fixed time step and a maximum number of iterations.
// The following post provides a lot of thoughts into how to build this
// loop: http://gafferongames.com/game-physics/fix-your-timestep/
CGFloat TIMESTEP_MSEC = 1;
// Velocity is based on seconds instead of milliseconds
CGFloat step = TIMESTEP_MSEC / 1000;
NSTimeInterval currentTime = CACurrentMediaTime();
NSInteger numSteps = floorf((currentTime - _animationCurrentTime) / step);
_animationCurrentTime = currentTime;
if (numSteps == 0) {
return;
}
CGFloat position = _lastPosition;
CGFloat velocity = _lastVelocity;
CGFloat tempPosition = _lastPosition;
CGFloat tempVelocity = _lastVelocity;
for (NSInteger i = 0; i < numSteps; ++i) {
// This is using RK4. A good blog post to understand how it works:
// http://gafferongames.com/game-physics/integration-basics/
CGFloat aVelocity = velocity;
CGFloat aAcceleration = _tension * (_toValue - tempPosition) - _friction * tempVelocity;
tempPosition = position + aVelocity * step / 2;
tempVelocity = velocity + aAcceleration * step / 2;
CGFloat bVelocity = tempVelocity;
CGFloat bAcceleration = _tension * (_toValue - tempPosition) - _friction * tempVelocity;
tempPosition = position + bVelocity * step / 2;
tempVelocity = velocity + bAcceleration * step / 2;
CGFloat cVelocity = tempVelocity;
CGFloat cAcceleration = _tension * (_toValue - tempPosition) - _friction * tempVelocity;
tempPosition = position + cVelocity * step / 2;
tempVelocity = velocity + cAcceleration * step / 2;
CGFloat dVelocity = tempVelocity;
CGFloat dAcceleration = _tension * (_toValue - tempPosition) - _friction * tempVelocity;
tempPosition = position + cVelocity * step / 2;
tempVelocity = velocity + cAcceleration * step / 2;
CGFloat dxdt = (aVelocity + 2 * (bVelocity + cVelocity) + dVelocity) / 6;
CGFloat dvdt = (aAcceleration + 2 * (bAcceleration + cAcceleration) + dAcceleration) / 6;
position += dxdt * step;
velocity += dvdt * step;
}
_lastPosition = position;
_lastVelocity = velocity;
[self onUpdate:position];
if (_animationHasFinished) {
return;
}
// Conditions for stopping the spring animation
BOOL isOvershooting = NO;
if (_overshootClamping && _tension != 0) {
if (_fromValue < _toValue) {
isOvershooting = position > _toValue;
} else {
isOvershooting = position < _toValue;
}
}
BOOL isVelocity = ABS(velocity) <= _restSpeedThreshold;
BOOL isDisplacement = YES;
if (_tension != 0) {
isDisplacement = ABS(_toValue - position) <= _restDisplacementThreshold;
}
if (isOvershooting || (isVelocity && isDisplacement)) {
if (_tension != 0) {
// Ensure that we end up with a round value
if (_animationHasFinished) {
return;
}
[self onUpdate:_toValue];
}
[self stopAnimation];
}
}
- (void)onUpdate:(CGFloat)outputValue
{
_valueNode.value = outputValue;
[_valueNode setNeedsUpdate];
}
- (void)cleanupAnimationUpdate
{
[_valueNode cleanupAnimationUpdate];
}
@end

View File

@ -12,7 +12,6 @@
13E501D41D07A644005F35D8 /* RCTViewPropertyMapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501C81D07A644005F35D8 /* RCTViewPropertyMapper.m */; };
13E501E81D07A6C9005F35D8 /* RCTAdditionAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501D71D07A6C9005F35D8 /* RCTAdditionAnimatedNode.m */; };
13E501E91D07A6C9005F35D8 /* RCTAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501D91D07A6C9005F35D8 /* RCTAnimatedNode.m */; };
13E501EA1D07A6C9005F35D8 /* RCTAnimationDriverNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501DB1D07A6C9005F35D8 /* RCTAnimationDriverNode.m */; };
13E501EB1D07A6C9005F35D8 /* RCTInterpolationAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501DD1D07A6C9005F35D8 /* RCTInterpolationAnimatedNode.m */; };
13E501EC1D07A6C9005F35D8 /* RCTMultiplicationAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501DF1D07A6C9005F35D8 /* RCTMultiplicationAnimatedNode.m */; };
13E501ED1D07A6C9005F35D8 /* RCTPropsAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501E11D07A6C9005F35D8 /* RCTPropsAnimatedNode.m */; };
@ -27,7 +26,6 @@
2D3B5EF61D9B0B4800451313 /* RCTDiffClampAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 193F64F31D776EC6004D1CAA /* RCTDiffClampAnimatedNode.m */; };
2D3B5EF71D9B0B4800451313 /* RCTAdditionAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501D71D07A6C9005F35D8 /* RCTAdditionAnimatedNode.m */; };
2D3B5EF81D9B0B4800451313 /* RCTAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501D91D07A6C9005F35D8 /* RCTAnimatedNode.m */; };
2D3B5EF91D9B0B4800451313 /* RCTAnimationDriverNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501DB1D07A6C9005F35D8 /* RCTAnimationDriverNode.m */; };
2D3B5EFA1D9B0B4800451313 /* RCTInterpolationAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501DD1D07A6C9005F35D8 /* RCTInterpolationAnimatedNode.m */; };
2D3B5EFB1D9B0B4800451313 /* RCTModuloAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 94DAE3F81D7334A70059942F /* RCTModuloAnimatedNode.m */; };
2D3B5EFC1D9B0B4800451313 /* RCTMultiplicationAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501DF1D07A6C9005F35D8 /* RCTMultiplicationAnimatedNode.m */; };
@ -36,6 +34,10 @@
2D3B5EFF1D9B0B4800451313 /* RCTTransformAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501E51D07A6C9005F35D8 /* RCTTransformAnimatedNode.m */; };
2D3B5F001D9B0B4800451313 /* RCTValueAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501E71D07A6C9005F35D8 /* RCTValueAnimatedNode.m */; };
5C9894951D999639008027DB /* RCTDivisionAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C9894941D999639008027DB /* RCTDivisionAnimatedNode.m */; };
944244D01DB962DA0032A02B /* RCTFrameAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 94C1294D1D4069170025F25C /* RCTFrameAnimation.m */; };
944244D11DB962DC0032A02B /* RCTSpringAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 94C1294F1D4069170025F25C /* RCTSpringAnimation.m */; };
94C129511D40692B0025F25C /* RCTFrameAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 94C1294D1D4069170025F25C /* RCTFrameAnimation.m */; };
94C129521D40692B0025F25C /* RCTSpringAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 94C1294F1D4069170025F25C /* RCTSpringAnimation.m */; };
94DAE3F91D7334A70059942F /* RCTModuloAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 94DAE3F81D7334A70059942F /* RCTModuloAnimatedNode.m */; };
/* End PBXBuildFile section */
@ -72,8 +74,6 @@
13E501D71D07A6C9005F35D8 /* RCTAdditionAnimatedNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAdditionAnimatedNode.m; sourceTree = "<group>"; };
13E501D81D07A6C9005F35D8 /* RCTAnimatedNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAnimatedNode.h; sourceTree = "<group>"; };
13E501D91D07A6C9005F35D8 /* RCTAnimatedNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAnimatedNode.m; sourceTree = "<group>"; };
13E501DA1D07A6C9005F35D8 /* RCTAnimationDriverNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAnimationDriverNode.h; sourceTree = "<group>"; };
13E501DB1D07A6C9005F35D8 /* RCTAnimationDriverNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAnimationDriverNode.m; sourceTree = "<group>"; };
13E501DC1D07A6C9005F35D8 /* RCTInterpolationAnimatedNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTInterpolationAnimatedNode.h; sourceTree = "<group>"; };
13E501DD1D07A6C9005F35D8 /* RCTInterpolationAnimatedNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTInterpolationAnimatedNode.m; sourceTree = "<group>"; };
13E501DE1D07A6C9005F35D8 /* RCTMultiplicationAnimatedNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMultiplicationAnimatedNode.h; sourceTree = "<group>"; };
@ -91,6 +91,11 @@
2D2A28201D9B03D100D4039D /* libRCTAnimation-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libRCTAnimation-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
5C9894931D999639008027DB /* RCTDivisionAnimatedNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDivisionAnimatedNode.h; sourceTree = "<group>"; };
5C9894941D999639008027DB /* RCTDivisionAnimatedNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDivisionAnimatedNode.m; sourceTree = "<group>"; };
94C1294A1D4069170025F25C /* RCTAnimationDriver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTAnimationDriver.h; sourceTree = "<group>"; };
94C1294C1D4069170025F25C /* RCTFrameAnimation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTFrameAnimation.h; sourceTree = "<group>"; };
94C1294D1D4069170025F25C /* RCTFrameAnimation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTFrameAnimation.m; sourceTree = "<group>"; };
94C1294E1D4069170025F25C /* RCTSpringAnimation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTSpringAnimation.h; sourceTree = "<group>"; };
94C1294F1D4069170025F25C /* RCTSpringAnimation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTSpringAnimation.m; sourceTree = "<group>"; };
94DAE3F71D7334A70059942F /* RCTModuloAnimatedNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTModuloAnimatedNode.h; sourceTree = "<group>"; };
94DAE3F81D7334A70059942F /* RCTModuloAnimatedNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuloAnimatedNode.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -132,8 +137,6 @@
13E501D71D07A6C9005F35D8 /* RCTAdditionAnimatedNode.m */,
13E501D81D07A6C9005F35D8 /* RCTAnimatedNode.h */,
13E501D91D07A6C9005F35D8 /* RCTAnimatedNode.m */,
13E501DA1D07A6C9005F35D8 /* RCTAnimationDriverNode.h */,
13E501DB1D07A6C9005F35D8 /* RCTAnimationDriverNode.m */,
13E501DC1D07A6C9005F35D8 /* RCTInterpolationAnimatedNode.h */,
13E501DD1D07A6C9005F35D8 /* RCTInterpolationAnimatedNode.m */,
94DAE3F71D7334A70059942F /* RCTModuloAnimatedNode.h */,
@ -161,12 +164,25 @@
13E501C81D07A644005F35D8 /* RCTViewPropertyMapper.m */,
13E501BD1D07A644005F35D8 /* RCTNativeAnimatedModule.h */,
13E501BE1D07A644005F35D8 /* RCTNativeAnimatedModule.m */,
94C129491D4069170025F25C /* Drivers */,
13E501D51D07A6C9005F35D8 /* Nodes */,
134814211AA4EA7D00B7C361 /* Products */,
2D2A28201D9B03D100D4039D /* libRCTAnimation-tvOS.a */,
);
sourceTree = "<group>";
};
94C129491D4069170025F25C /* Drivers */ = {
isa = PBXGroup;
children = (
94C1294A1D4069170025F25C /* RCTAnimationDriver.h */,
94C1294C1D4069170025F25C /* RCTFrameAnimation.h */,
94C1294D1D4069170025F25C /* RCTFrameAnimation.m */,
94C1294E1D4069170025F25C /* RCTSpringAnimation.h */,
94C1294F1D4069170025F25C /* RCTSpringAnimation.m */,
);
path = Drivers;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -255,11 +271,12 @@
2D3B5EF81D9B0B4800451313 /* RCTAnimatedNode.m in Sources */,
2D3B5EFE1D9B0B4800451313 /* RCTStyleAnimatedNode.m in Sources */,
2D3B5EFA1D9B0B4800451313 /* RCTInterpolationAnimatedNode.m in Sources */,
2D3B5EF91D9B0B4800451313 /* RCTAnimationDriverNode.m in Sources */,
2D3B5EFF1D9B0B4800451313 /* RCTTransformAnimatedNode.m in Sources */,
2D3B5EFC1D9B0B4800451313 /* RCTMultiplicationAnimatedNode.m in Sources */,
2D3B5EFD1D9B0B4800451313 /* RCTPropsAnimatedNode.m in Sources */,
2D3B5EF31D9B0B3400451313 /* RCTViewPropertyMapper.m in Sources */,
944244D01DB962DA0032A02B /* RCTFrameAnimation.m in Sources */,
944244D11DB962DC0032A02B /* RCTSpringAnimation.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -267,6 +284,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
94C129511D40692B0025F25C /* RCTFrameAnimation.m in Sources */,
94C129521D40692B0025F25C /* RCTSpringAnimation.m in Sources */,
13E501F01D07A6C9005F35D8 /* RCTValueAnimatedNode.m in Sources */,
94DAE3F91D7334A70059942F /* RCTModuloAnimatedNode.m in Sources */,
193F64F41D776EC6004D1CAA /* RCTDiffClampAnimatedNode.m in Sources */,
@ -279,7 +298,6 @@
13E501EB1D07A6C9005F35D8 /* RCTInterpolationAnimatedNode.m in Sources */,
13E501E81D07A6C9005F35D8 /* RCTAdditionAnimatedNode.m in Sources */,
5C9894951D999639008027DB /* RCTDivisionAnimatedNode.m in Sources */,
13E501EA1D07A6C9005F35D8 /* RCTAnimationDriverNode.m in Sources */,
13E501EF1D07A6C9005F35D8 /* RCTTransformAnimatedNode.m in Sources */,
13E501D41D07A644005F35D8 /* RCTViewPropertyMapper.m in Sources */,
);

View File

@ -9,7 +9,9 @@
#import "RCTNativeAnimatedModule.h"
#import "RCTAdditionAnimatedNode.h"
#import "RCTAnimationDriverNode.h"
#import "RCTAnimationDriver.h"
#import "RCTFrameAnimation.h"
#import "RCTSpringAnimation.h"
#import "RCTAnimationUtils.h"
#import "RCTBridge.h"
#import "RCTConvert.h"
@ -27,9 +29,9 @@
@implementation RCTNativeAnimatedModule
{
NSMutableDictionary<NSNumber *, RCTAnimatedNode *> *_animationNodes;
NSMutableDictionary<NSNumber *, RCTAnimationDriverNode *> *_animationDrivers;
NSMutableSet<RCTAnimationDriverNode *> *_activeAnimations;
NSMutableSet<RCTAnimationDriverNode *> *_finishedAnimations;
NSMutableDictionary<NSNumber *, id<RCTAnimationDriver>> *_animationDrivers;
NSMutableSet<id<RCTAnimationDriver>> *_activeAnimations;
NSMutableSet<id<RCTAnimationDriver>> *_finishedAnimations;
NSMutableSet<RCTValueAnimatedNode *> *_updatedValueNodes;
NSMutableSet<RCTPropsAnimatedNode *> *_propAnimationNodes;
CADisplayLink *_displayLink;
@ -87,6 +89,7 @@ RCT_EXPORT_METHOD(createAnimatedNode:(nonnull NSNumber *)tag
}
RCTAnimatedNode *node = [[nodeClass alloc] initWithTag:tag config:config];
_animationNodes[tag] = node;
if ([node isKindOfClass:[RCTPropsAnimatedNode class]]) {
@ -129,24 +132,28 @@ RCT_EXPORT_METHOD(startAnimatingNode:(nonnull NSNumber *)animationId
config:(NSDictionary<NSString *, id> *)config
endCallback:(RCTResponseSenderBlock)callBack)
{
if (RCT_DEBUG && ![config[@"type"] isEqual:@"frames"]) {
RCTValueAnimatedNode *valueNode = (RCTValueAnimatedNode *)_animationNodes[nodeTag];
NSString *type = config[@"type"];
id<RCTAnimationDriver>animationDriver;
if ([type isEqual:@"frames"]) {
animationDriver = [[RCTFrameAnimation alloc] initWithId:animationId
config:config
forNode:valueNode
callBack:callBack];
} else if ([type isEqual:@"spring"]) {
animationDriver = [[RCTSpringAnimation alloc] initWithId:animationId
config:config
forNode:valueNode
callBack:callBack];
} else {
RCTLogError(@"Unsupported animation type: %@", config[@"type"]);
return;
}
NSTimeInterval delay = [RCTConvert double:config[@"delay"]];
NSNumber *toValue = [RCTConvert NSNumber:config[@"toValue"]] ?: @1;
NSArray<NSNumber *> *frames = [RCTConvert NSNumberArray:config[@"frames"]];
RCTValueAnimatedNode *valueNode = (RCTValueAnimatedNode *)_animationNodes[nodeTag];
RCTAnimationDriverNode *animationDriver =
[[RCTAnimationDriverNode alloc] initWithId:animationId
delay:delay
toValue:toValue.doubleValue
frames:frames
forNode:valueNode
callBack:callBack];
[_activeAnimations addObject:animationDriver];
_animationDrivers[animationId] = animationDriver;
[animationDriver startAnimation];
@ -155,7 +162,7 @@ RCT_EXPORT_METHOD(startAnimatingNode:(nonnull NSNumber *)animationId
RCT_EXPORT_METHOD(stopAnimation:(nonnull NSNumber *)animationId)
{
RCTAnimationDriverNode *driver = _animationDrivers[animationId];
id<RCTAnimationDriver>driver = _animationDrivers[animationId];
if (driver) {
[driver removeAnimation];
[_animationDrivers removeObjectForKey:animationId];
@ -172,6 +179,7 @@ RCT_EXPORT_METHOD(setAnimatedNodeValue:(nonnull NSNumber *)nodeTag
RCTLogError(@"Not a value node.");
return;
}
RCTValueAnimatedNode *valueNode = (RCTValueAnimatedNode *)node;
valueNode.value = value.floatValue;
[valueNode setNeedsUpdate];
@ -272,7 +280,7 @@ RCT_EXPORT_METHOD(stopListeningToAnimatedNodeValue:(nonnull NSNumber *)tag)
{
// Step Current active animations
// This also recursively marks children nodes as needing update
for (RCTAnimationDriverNode *animationDriver in _activeAnimations) {
for (id<RCTAnimationDriver>animationDriver in _activeAnimations) {
[animationDriver stepAnimation];
}
@ -283,7 +291,7 @@ RCT_EXPORT_METHOD(stopListeningToAnimatedNodeValue:(nonnull NSNumber *)tag)
}
// Cleanup nodes and prepare for next cycle. Remove updated nodes from bucket.
for (RCTAnimationDriverNode *driverNode in _activeAnimations) {
for (id<RCTAnimationDriver>driverNode in _activeAnimations) {
[driverNode cleanupAnimationUpdate];
}
for (RCTValueAnimatedNode *valueNode in _updatedValueNodes) {
@ -291,13 +299,13 @@ RCT_EXPORT_METHOD(stopListeningToAnimatedNodeValue:(nonnull NSNumber *)tag)
}
[_updatedValueNodes removeAllObjects];
for (RCTAnimationDriverNode *driverNode in _activeAnimations) {
for (id<RCTAnimationDriver>driverNode in _activeAnimations) {
if (driverNode.animationHasFinished) {
[driverNode removeAnimation];
[_finishedAnimations addObject:driverNode];
}
}
for (RCTAnimationDriverNode *driverNode in _finishedAnimations) {
for (id<RCTAnimationDriver>driverNode in _finishedAnimations) {
[_activeAnimations removeObject:driverNode];
[_animationDrivers removeObjectForKey:driverNode.animationId];
}