[ReactNative] Delete AnimationExperimental and AnimationUtils

Summary:
AnimationExperimental is deprecated and being removed in favor of the Animated library.
Refer to https://facebook.github.io/react-native/docs/animations.html for information on how to use Animated
and https://facebook.github.io/react-native/docs/animations.html#animationexperimental-deprecated for the reasoning behind this.
This commit is contained in:
Michael Mitchell 2015-07-24 10:21:07 -07:00
parent 000ab1139f
commit a1612a7dd2
5 changed files with 0 additions and 909 deletions

View File

@ -1,99 +0,0 @@
/**
* 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.
*
* @providesModule AnimationExperimental
*/
'use strict';
var RCTAnimationManager = require('NativeModules').AnimationExperimentalManager;
if (!RCTAnimationManager) {
// AnimationExperimental isn't available internally - this is a temporary
// workaround to enable its availability to be determined at runtime.
// For Flow let's pretend like we always export AnimationExperimental
// so all our users don't need to do null checks
module.exports = null;
} else {
var React = require('React');
var AnimationUtils = require('AnimationUtils');
type EasingFunction = (t: number) => number;
var Properties = {
opacity: true,
position: true,
positionX: true,
positionY: true,
rotation: true,
scaleXY: true,
};
type ValueType = number | Array<number> | {[key: string]: number};
/**
* This is an experimental module that is under development, incomplete,
* potentially buggy, not used in any production apps, and will probably change
* in non-backward compatible ways.
*
* Use at your own risk.
*/
var AnimationExperimental = {
startAnimation: function(
anim: {
node: any;
duration: number;
easing: ($Enum<typeof AnimationUtils.Defaults> | EasingFunction);
property: $Enum<typeof Properties>;
toValue: ValueType;
fromValue?: ValueType;
delay?: number;
},
callback?: ?(finished: bool) => void
): number {
var nodeHandle = React.findNodeHandle(anim.node);
var easingSample = AnimationUtils.evaluateEasingFunction(
anim.duration,
anim.easing
);
var tag: number = AnimationUtils.allocateTag();
var props = {};
props[anim.property] = {to: anim.toValue};
RCTAnimationManager.startAnimation(
nodeHandle,
tag,
anim.duration,
anim.delay,
easingSample,
props,
callback
);
return tag;
},
stopAnimation: function(tag: number) {
RCTAnimationManager.stopAnimation(tag);
},
};
if (__DEV__) {
if (RCTAnimationManager && RCTAnimationManager.Properties) {
var a = Object.keys(Properties);
var b = RCTAnimationManager.Properties;
var diff = a.filter((i) => b.indexOf(i) < 0).concat(
b.filter((i) => a.indexOf(i) < 0)
);
if (diff.length > 0) {
throw new Error('JS animation properties don\'t match native properties.' +
JSON.stringify(diff, null, ' '));
}
}
}
module.exports = AnimationExperimental;
}

View File

@ -1,244 +0,0 @@
/**
* 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.
*
* The easing functions come from the the jQuery UI project.
* See http://api.jqueryui.com/easings/
* Copyright jQuery Foundation and other contributors, https://jquery.org/
* Copyright (c) 2008 George McGinley Smith
* Copyright (c) 2001 Robert Penner
*
* @providesModule AnimationUtils
* @flow
*/
'use strict';
type EasingFunction = (t: number) => number;
var defaults = {
linear: function(t: number): number {
return t;
},
easeInQuad: function(t: number): number {
return t * t;
},
easeOutQuad: function(t: number): number {
return -t * (t - 2);
},
easeInOutQuad: function(t: number): number {
t = t * 2;
if (t < 1) {
return 0.5 * t * t;
}
return -((t - 1) * (t - 3) - 1) / 2;
},
easeInCubic: function(t: number): number {
return t * t * t;
},
easeOutCubic: function(t: number): number {
t -= 1;
return t * t * t + 1;
},
easeInOutCubic: function(t: number): number {
t *= 2;
if (t < 1) {
return 0.5 * t * t * t;
}
t -= 2;
return (t * t * t + 2) / 2;
},
easeInQuart: function(t: number): number {
return t * t * t * t;
},
easeOutQuart: function(t: number): number {
t -= 1;
return -(t * t * t * t - 1);
},
easeInOutQuart: function(t: number): number {
t *= 2;
if (t < 1) {
return 0.5 * t * t * t * t;
}
t -= 2;
return -(t * t * t * t - 2) / 2;
},
easeInQuint: function(t: number): number {
return t * t * t * t * t;
},
easeOutQuint: function(t: number): number {
t -= 1;
return t * t * t * t * t + 1;
},
easeInOutQuint: function(t: number): number {
t *= 2;
if (t < 1) {
return (t * t * t * t * t) / 2;
}
t -= 2;
return (t * t * t * t * t + 2) / 2;
},
easeInSine: function(t: number): number {
return -Math.cos(t * (Math.PI / 2)) + 1;
},
easeOutSine: function(t: number): number {
return Math.sin(t * (Math.PI / 2));
},
easeInOutSine: function(t: number): number {
return -(Math.cos(Math.PI * t) - 1) / 2;
},
easeInExpo: function(t: number): number {
return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1));
},
easeOutExpo: function(t: number): number {
return (t === 1) ? 1 : (-Math.pow(2, -10 * t) + 1);
},
easeInOutExpo: function(t: number): number {
if (t === 0) {
return 0;
}
if (t === 1) {
return 1;
}
t *= 2;
if (t < 1) {
return 0.5 * Math.pow(2, 10 * (t - 1));
}
return (-Math.pow(2, -10 * (t - 1)) + 2) / 2;
},
easeInCirc: function(t: number): number {
return -(Math.sqrt(1 - t * t) - 1);
},
easeOutCirc: function(t: number): number {
t -= 1;
return Math.sqrt(1 - t * t);
},
easeInOutCirc: function(t: number): number {
t *= 2;
if (t < 1) {
return -(Math.sqrt(1 - t * t) - 1) / 2;
}
t -= 2;
return (Math.sqrt(1 - t * t) + 1) / 2;
},
easeInElastic: function(t: number): number {
var s = 1.70158;
var p = 0.3;
if (t === 0) {
return 0;
}
if (t === 1) {
return 1;
}
var s = p / (2 * Math.PI) * Math.asin(1);
t -= 1;
return -(Math.pow(2, 10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
},
easeOutElastic: function(t: number): number {
var s = 1.70158;
var p = 0.3;
if (t === 0) {
return 0;
}
if (t === 1) {
return 1;
}
var s = p / (2 * Math.PI) * Math.asin(1);
return Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1;
},
easeInOutElastic: function(t: number): number {
var s = 1.70158;
var p = 0.3 * 1.5;
if (t === 0) {
return 0;
}
t *= 2;
if (t === 2) {
return 1;
}
var s = p / (2 * Math.PI) * Math.asin(1);
if (t < 1) {
t -= 1;
return -(Math.pow(2, 10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p)) / 2;
}
t -= 1;
return Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) / 2 + 1;
},
easeInBack: function(t: number): number {
var s = 1.70158;
return t * t * ((s + 1) * t - s);
},
easeOutBack: function(t: number): number {
var s = 1.70158;
t -= 1;
return (t * t * ((s + 1) * t + s) + 1);
},
easeInOutBack: function(t: number): number {
var s = 1.70158 * 1.525;
t *= 2;
if (t < 1) {
return (t * t * ((s + 1) * t - s)) / 2;
}
t -= 2;
return (t * t * ((s + 1) * t + s) + 2) / 2;
},
easeInBounce: function(t: number): number {
return 1 - this.easeOutBounce(1 - t);
},
easeOutBounce: function(t: number): number {
if (t < (1 / 2.75)) {
return 7.5625 * t * t;
} else if (t < (2 / 2.75)) {
t -= 1.5 / 2.75;
return 7.5625 * t * t + 0.75;
} else if (t < (2.5 / 2.75)) {
t -= 2.25 / 2.75;
return 7.5625 * t * t + 0.9375;
} else {
t -= 2.625 / 2.75;
return 7.5625 * t * t + 0.984375;
}
},
easeInOutBounce: function(t: number): number {
if (t < 0.5) {
return this.easeInBounce(t * 2) / 2;
}
return this.easeOutBounce(t * 2 - 1) / 2 + 0.5;
},
};
var ticksPerSecond = 60;
var lastUsedTag = 0;
module.exports = {
/**
* Returns a new unique animation tag.
*/
allocateTag: function(): number {
return ++lastUsedTag;
},
/**
* Returns the values of the easing function at discrete ticks (60 per second).
*/
evaluateEasingFunction: function(duration: number, easing: string | EasingFunction): Array<number> {
if (typeof easing === 'string') {
easing = defaults[easing] || defaults.easeOutQuad;
}
var tickCount = Math.round(duration * ticksPerSecond / 1000);
var samples = [];
if (tickCount > 0) {
for (var i = 0; i <= tickCount; i++) {
samples.push(easing.call(defaults, i / tickCount));
}
}
return samples;
},
Defaults: defaults,
};

View File

@ -1,250 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
13BE3DEE1AC21097009241FE /* RCTAnimationExperimentalManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BE3DED1AC21097009241FE /* RCTAnimationExperimentalManager.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
58B511D91A9E6C8500147676 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
134814201AA4EA6300B7C361 /* libRCTAnimationExperimental.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTAnimationExperimental.a; sourceTree = BUILT_PRODUCTS_DIR; };
13BE3DEC1AC21097009241FE /* RCTAnimationExperimentalManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAnimationExperimentalManager.h; sourceTree = "<group>"; };
13BE3DED1AC21097009241FE /* RCTAnimationExperimentalManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAnimationExperimentalManager.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
58B511D81A9E6C8500147676 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
134814211AA4EA7D00B7C361 /* Products */ = {
isa = PBXGroup;
children = (
134814201AA4EA6300B7C361 /* libRCTAnimationExperimental.a */,
);
name = Products;
sourceTree = "<group>";
};
58B511D21A9E6C8500147676 = {
isa = PBXGroup;
children = (
13BE3DEC1AC21097009241FE /* RCTAnimationExperimentalManager.h */,
13BE3DED1AC21097009241FE /* RCTAnimationExperimentalManager.m */,
134814211AA4EA7D00B7C361 /* Products */,
);
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
58B511DA1A9E6C8500147676 /* RCTAnimationExperimental */ = {
isa = PBXNativeTarget;
buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTAnimationExperimental" */;
buildPhases = (
58B511D71A9E6C8500147676 /* Sources */,
58B511D81A9E6C8500147676 /* Frameworks */,
58B511D91A9E6C8500147676 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = RCTAnimationExperimental;
productName = RCTDataManager;
productReference = 134814201AA4EA6300B7C361 /* libRCTAnimationExperimental.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
58B511D31A9E6C8500147676 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0610;
ORGANIZATIONNAME = Facebook;
TargetAttributes = {
58B511DA1A9E6C8500147676 = {
CreatedOnToolsVersion = 6.1.1;
};
};
};
buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTAnimationExperimental" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 58B511D21A9E6C8500147676;
productRefGroup = 58B511D21A9E6C8500147676;
projectDirPath = "";
projectRoot = "";
targets = (
58B511DA1A9E6C8500147676 /* RCTAnimationExperimental */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
58B511D71A9E6C8500147676 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
13BE3DEE1AC21097009241FE /* RCTAnimationExperimentalManager.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
58B511ED1A9E6C8500147676 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
58B511EE1A9E6C8500147676 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
58B511F01A9E6C8500147676 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../React/**",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = RCTAnimationExperimental;
SKIP_INSTALL = YES;
};
name = Debug;
};
58B511F11A9E6C8500147676 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../React/**",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = RCTAnimationExperimental;
SKIP_INSTALL = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTAnimationExperimental" */ = {
isa = XCConfigurationList;
buildConfigurations = (
58B511ED1A9E6C8500147676 /* Debug */,
58B511EE1A9E6C8500147676 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTAnimationExperimental" */ = {
isa = XCConfigurationList;
buildConfigurations = (
58B511F01A9E6C8500147676 /* Debug */,
58B511F11A9E6C8500147676 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 58B511D31A9E6C8500147676 /* Project object */;
}

View File

@ -1,17 +0,0 @@
/**
* 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 <Accelerate/Accelerate.h>
#import <Foundation/Foundation.h>
#import "RCTBridgeModule.h"
@interface RCTAnimationExperimentalManager : NSObject <RCTBridgeModule>
@end

View File

@ -1,299 +0,0 @@
/**
* 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 "RCTAnimationExperimentalManager.h"
#import <UIKit/UIKit.h>
#import "RCTSparseArray.h"
#import "RCTUIManager.h"
#import "RCTUtils.h"
#if CGFLOAT_IS_DOUBLE
#define CG_APPEND(PREFIX, SUFFIX_F, SUFFIX_D) PREFIX##SUFFIX_D
#else
#define CG_APPEND(PREFIX, SUFFIX_F, SUFFIX_D) PREFIX##SUFFIX_F
#endif
@implementation RCTAnimationExperimentalManager
{
RCTSparseArray *_animationRegistry; // Main thread only; animation tag -> view tag
RCTSparseArray *_callbackRegistry; // Main thread only; animation tag -> callback
NSDictionary *_keypathMapping;
}
RCT_EXPORT_MODULE()
@synthesize bridge = _bridge;
- (instancetype)init
{
if ((self = [super init])) {
_animationRegistry = [[RCTSparseArray alloc] init];
_callbackRegistry = [[RCTSparseArray alloc] init];
_keypathMapping = @{
@"opacity": @{
@"keypath": @"opacity",
@"type": @"NSNumber",
},
@"position": @{
@"keypath": @"position",
@"type": @"CGPoint",
},
@"positionX": @{
@"keypath": @"position.x",
@"type": @"NSNumber",
},
@"positionY": @{
@"keypath": @"position.y",
@"type": @"NSNumber",
},
@"rotation": @{
@"keypath": @"transform.rotation.z",
@"type": @"NSNumber",
},
@"scaleXY": @{
@"keypath": @"transform.scale",
@"type": @"CGPoint",
},
};
}
return self;
}
- (dispatch_queue_t)methodQueue
{
return _bridge.uiManager.methodQueue;
}
- (id (^)(CGFloat))interpolateFrom:(CGFloat[])fromArray to:(CGFloat[])toArray count:(NSUInteger)count typeName:(const char *)typeName
{
if (count == 1) {
CGFloat from = *fromArray, to = *toArray, delta = to - from;
return ^(CGFloat t) {
return @(from + t * delta);
};
}
CG_APPEND(vDSP_vsub,,D)(fromArray, 1, toArray, 1, toArray, 1, count);
const size_t size = count * sizeof(CGFloat);
NSData *deltaData = [NSData dataWithBytes:toArray length:size];
NSData *fromData = [NSData dataWithBytes:fromArray length:size];
return ^(CGFloat t) {
const CGFloat *delta = deltaData.bytes;
const CGFloat *_fromArray = fromData.bytes;
CGFloat value[count];
CG_APPEND(vDSP_vma,,D)(delta, 1, &t, 0, _fromArray, 1, value, 1, count);
return [NSValue valueWithBytes:value objCType:typeName];
};
}
static void RCTInvalidAnimationProp(RCTSparseArray *callbacks, NSNumber *tag, NSString *key, id value)
{
RCTResponseSenderBlock callback = callbacks[tag];
RCTLogError(@"Invalid animation property `%@ = %@`", key, value);
if (callback) {
callback(@[@NO]);
callbacks[tag] = nil;
}
[CATransaction commit];
return;
}
RCT_EXPORT_METHOD(startAnimation:(NSNumber *)reactTag
animationTag:(NSNumber *)animationTag
duration:(NSTimeInterval)duration
delay:(NSTimeInterval)delay
easingSample:(NSNumberArray *)easingSample
properties:(NSDictionary *)properties
callback:(RCTResponseSenderBlock)callback)
{
__weak RCTAnimationExperimentalManager *weakSelf = self;
[_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
RCTAnimationExperimentalManager *strongSelf = weakSelf;
UIView *view = viewRegistry[reactTag];
if (!view) {
RCTLogWarn(@"React tag #%@ is not registered with the view registry", reactTag);
return;
}
__block BOOL completionBlockSet = NO;
[CATransaction begin];
for (NSString *prop in properties) {
NSString *keypath = _keypathMapping[prop][@"keypath"];
id obj = properties[prop][@"to"];
if (!keypath) {
return RCTInvalidAnimationProp(strongSelf->_callbackRegistry, animationTag, keypath, obj);
}
NSValue *toValue = nil;
if ([keypath isEqualToString:@"transform.scale"]) {
CGPoint point = [RCTConvert CGPoint:obj];
if (point.x != point.y) {
return RCTInvalidAnimationProp(strongSelf->_callbackRegistry, animationTag, keypath, obj);
}
toValue = @(point.x);
} else if ([obj respondsToSelector:@selector(count)]) {
switch ([obj count]) {
case 2:
if (![obj respondsToSelector:@selector(objectForKeyedSubscript:)] || obj[@"x"]) {
toValue = [NSValue valueWithCGPoint:[RCTConvert CGPoint:obj]];
} else {
toValue = [NSValue valueWithCGSize:[RCTConvert CGSize:obj]];
}
break;
case 4:
toValue = [NSValue valueWithCGRect:[RCTConvert CGRect:obj]];
break;
case 16:
toValue = [NSValue valueWithCGAffineTransform:[RCTConvert CGAffineTransform:obj]];
break;
default:
return RCTInvalidAnimationProp(strongSelf->_callbackRegistry, animationTag, keypath, obj);
}
} else if (![obj respondsToSelector:@selector(objCType)]) {
return RCTInvalidAnimationProp(strongSelf->_callbackRegistry, animationTag, keypath, obj);
}
if (!toValue) {
toValue = obj;
}
const char *typeName = toValue.objCType;
size_t count;
switch (typeName[0]) {
case 'i':
case 'I':
case 's':
case 'S':
case 'l':
case 'L':
case 'q':
case 'Q':
count = 1;
break;
default: {
NSUInteger size;
NSGetSizeAndAlignment(typeName, &size, NULL);
count = size / sizeof(CGFloat);
break;
}
}
CGFloat toFields[count];
switch (typeName[0]) {
#define CASE(encoding, type) \
case encoding: { \
type value; \
[toValue getValue:&value]; \
toFields[0] = value; \
break; \
}
CASE('i', int)
CASE('I', unsigned int)
CASE('s', short)
CASE('S', unsigned short)
CASE('l', long)
CASE('L', unsigned long)
CASE('q', long long)
CASE('Q', unsigned long long)
#undef CASE
default:
[toValue getValue:toFields];
break;
}
NSValue *fromValue = [view.layer.presentationLayer valueForKeyPath:keypath];
#if !CGFLOAT_IS_DOUBLE
if ([fromValue isKindOfClass:[NSNumber class]]) {
fromValue = [NSNumber numberWithFloat:[(NSNumber *)fromValue doubleValue]];
}
#endif
CGFloat fromFields[count];
[fromValue getValue:fromFields];
id (^interpolationBlock)(CGFloat t) = [strongSelf interpolateFrom:fromFields to:toFields count:count typeName:typeName];
NSMutableArray *sampledValues = [NSMutableArray arrayWithCapacity:easingSample.count];
for (NSNumber *sample in easingSample) {
CGFloat t = sample.CG_APPEND(, floatValue, doubleValue);
[sampledValues addObject:interpolationBlock(t)];
}
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:keypath];
animation.beginTime = CACurrentMediaTime() + delay;
animation.duration = duration;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animation.values = sampledValues;
@try {
[view.layer setValue:toValue forKey:keypath];
NSString *animationKey = [@"RCT" stringByAppendingString:RCTJSONStringify(@{@"tag": animationTag, @"key": keypath}, nil)];
if (!completionBlockSet) {
strongSelf->_callbackRegistry[animationTag] = callback;
[CATransaction setCompletionBlock:^{
RCTResponseSenderBlock cb = strongSelf->_callbackRegistry[animationTag];
if (cb) {
cb(@[@YES]);
strongSelf->_callbackRegistry[animationTag] = nil;
}
}];
completionBlockSet = YES;
}
[view.layer addAnimation:animation forKey:animationKey];
}
@catch (NSException *exception) {
return RCTInvalidAnimationProp(strongSelf->_callbackRegistry, animationTag, keypath, toValue);
}
}
[CATransaction commit];
strongSelf->_animationRegistry[animationTag] = reactTag;
}];
}
RCT_EXPORT_METHOD(stopAnimation:(NSNumber *)animationTag)
{
__weak RCTAnimationExperimentalManager *weakSelf = self;
[_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
RCTAnimationExperimentalManager *strongSelf = weakSelf;
NSNumber *reactTag = strongSelf->_animationRegistry[animationTag];
if (!reactTag) {
return;
}
UIView *view = viewRegistry[reactTag];
for (NSString *animationKey in view.layer.animationKeys) {
if ([animationKey hasPrefix:@"RCT{"]) {
NSDictionary *data = RCTJSONParse([animationKey substringFromIndex:3], nil);
if (animationTag.integerValue == [data[@"tag"] integerValue]) {
[view.layer removeAnimationForKey:animationKey];
}
}
}
RCTResponseSenderBlock cb = strongSelf->_callbackRegistry[animationTag];
if (cb) {
cb(@[@NO]);
strongSelf->_callbackRegistry[animationTag] = nil;
}
strongSelf->_animationRegistry[animationTag] = nil;
}];
}
- (NSDictionary *)constantsToExport
{
return @{@"Properties": [_keypathMapping allKeys] };
}
@end