[ReactNative] fixup AnimationExperimental a bit

This commit is contained in:
Spencer Ahrens 2015-04-08 14:09:24 -07:00
parent bd7b9da64a
commit 9ea0002774
7 changed files with 223 additions and 157 deletions

View File

@ -73,19 +73,29 @@ class Tile extends React.Component {
if (tile.isNew()) { if (tile.isNew()) {
offset.opacity = 0; offset.opacity = 0;
} else { } else {
var point = [ var point = {
animationPosition(tile.toColumn()), x: animationPosition(tile.toColumn()),
animationPosition(tile.toRow()), y: animationPosition(tile.toRow()),
]; };
AnimationExperimental.startAnimation(this.refs['this'], 100, 0, 'easeInOutQuad', {position: point}); AnimationExperimental.startAnimation({
node: this.refs['this'],
duration: 100,
easing: 'easeInOutQuad',
property: 'position',
toValue: point,
});
} }
return offset; return offset;
} }
componentDidMount() { componentDidMount() {
AnimationExperimental.startAnimation(this.refs['this'], 100, 0, 'easeInOutQuad', {opacity: 1}); AnimationExperimental.startAnimation({
node: this.refs['this'],
duration: 100,
easing: 'easeInOutQuad',
property: 'opacity',
toValue: 1,
});
} }
render() { render() {

View File

@ -16,6 +16,17 @@ var AnimationUtils = require('AnimationUtils');
type EasingFunction = (t: number) => number; 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, * This is an experimental module that is under development, incomplete,
* potentially buggy, not used in any production apps, and will probably change * potentially buggy, not used in any production apps, and will probably change
@ -24,24 +35,34 @@ type EasingFunction = (t: number) => number;
* Use at your own risk. * Use at your own risk.
*/ */
var AnimationExperimental = { var AnimationExperimental = {
Mixin: require('AnimationExperimentalMixin'),
startAnimation: function( startAnimation: function(
node: any, anim: {
duration: number, node: any;
delay: number, duration: number;
easing: (string | EasingFunction), easing: ($Enum<typeof AnimationUtils.Defaults> | EasingFunction);
properties: {[key: string]: any} property: $Enum<typeof Properties>;
toValue: ValueType;
fromValue?: ValueType;
delay?: number;
},
callback?: ?(finished: bool) => void
): number { ): number {
var nodeHandle = +node.getNodeHandle(); var nodeHandle = anim.node.getNodeHandle();
var easingSample = AnimationUtils.evaluateEasingFunction(duration, easing); var easingSample = AnimationUtils.evaluateEasingFunction(
var tag: number = RCTAnimationManager.startAnimation( anim.duration,
anim.easing
);
var tag: number = AnimationUtils.allocateTag();
var props = {};
props[anim.property] = {to: anim.toValue};
RCTAnimationManager.startAnimation(
nodeHandle, nodeHandle,
AnimationUtils.allocateTag(), tag,
duration, anim.duration,
delay, anim.delay,
easingSample, easingSample,
properties props,
callback
); );
return tag; return tag;
}, },
@ -51,4 +72,18 @@ var AnimationExperimental = {
}, },
}; };
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; module.exports = AnimationExperimental;

View File

@ -1,58 +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 AnimationExperimentalMixin
* @flow
*/
'use strict';
var AnimationUtils = require('AnimationUtils');
var RCTAnimationManager = require('NativeModules').AnimationExperimentalManager;
var invariant = require('invariant');
type EasingFunction = (t: number) => 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 AnimationExperimentalMixin = {
getInitialState: function(): Object {
return {};
},
startAnimation: function(
refKey: string,
duration: number,
delay: number,
easing: (string | EasingFunction),
properties: {[key: string]: any}
): number {
var ref = this.refs[refKey];
invariant(
ref,
'Invalid refKey ' + refKey + '; ' +
'valid refs: ' + JSON.stringify(Object.keys(this.refs))
);
var nodeHandle = +ref.getNodeHandle();
var easingSample = AnimationUtils.evaluateEasingFunction(duration, easing);
var tag: number = RCTAnimationManager.startAnimation(nodeHandle, AnimationUtils.allocateTag(), duration, delay, easingSample, properties);
return tag;
},
stopAnimation: function(tag: number) {
RCTAnimationManager.stopAnimation(tag);
},
};
module.exports = AnimationExperimentalMixin;

View File

@ -20,27 +20,27 @@
type EasingFunction = (t: number) => number; type EasingFunction = (t: number) => number;
var defaults = { var defaults = {
easeInQuad: function(t) { easeInQuad: function(t: number): number {
return t * t; return t * t;
}, },
easeOutQuad: function(t) { easeOutQuad: function(t: number): number {
return -t * (t - 2); return -t * (t - 2);
}, },
easeInOutQuad: function(t) { easeInOutQuad: function(t: number): number {
t = t * 2; t = t * 2;
if (t < 1) { if (t < 1) {
return 0.5 * t * t; return 0.5 * t * t;
} }
return -((t - 1) * (t - 3) - 1) / 2; return -((t - 1) * (t - 3) - 1) / 2;
}, },
easeInCubic: function(t) { easeInCubic: function(t: number): number {
return t * t * t; return t * t * t;
}, },
easeOutCubic: function(t) { easeOutCubic: function(t: number): number {
t -= 1; t -= 1;
return t * t * t + 1; return t * t * t + 1;
}, },
easeInOutCubic: function(t) { easeInOutCubic: function(t: number): number {
t *= 2; t *= 2;
if (t < 1) { if (t < 1) {
return 0.5 * t * t * t; return 0.5 * t * t * t;
@ -48,14 +48,14 @@ var defaults = {
t -= 2; t -= 2;
return (t * t * t + 2) / 2; return (t * t * t + 2) / 2;
}, },
easeInQuart: function(t) { easeInQuart: function(t: number): number {
return t * t * t * t; return t * t * t * t;
}, },
easeOutQuart: function(t) { easeOutQuart: function(t: number): number {
t -= 1; t -= 1;
return -(t * t * t * t - 1); return -(t * t * t * t - 1);
}, },
easeInOutQuart: function(t) { easeInOutQuart: function(t: number): number {
t *= 2; t *= 2;
if (t < 1) { if (t < 1) {
return 0.5 * t * t * t * t; return 0.5 * t * t * t * t;
@ -63,14 +63,14 @@ var defaults = {
t -= 2; t -= 2;
return -(t * t * t * t - 2) / 2; return -(t * t * t * t - 2) / 2;
}, },
easeInQuint: function(t) { easeInQuint: function(t: number): number {
return t * t * t * t * t; return t * t * t * t * t;
}, },
easeOutQuint: function(t) { easeOutQuint: function(t: number): number {
t -= 1; t -= 1;
return t * t * t * t * t + 1; return t * t * t * t * t + 1;
}, },
easeInOutQuint: function(t) { easeInOutQuint: function(t: number): number {
t *= 2; t *= 2;
if (t < 1) { if (t < 1) {
return (t * t * t * t * t) / 2; return (t * t * t * t * t) / 2;
@ -78,22 +78,22 @@ var defaults = {
t -= 2; t -= 2;
return (t * t * t * t * t + 2) / 2; return (t * t * t * t * t + 2) / 2;
}, },
easeInSine: function(t) { easeInSine: function(t: number): number {
return -Math.cos(t * (Math.PI / 2)) + 1; return -Math.cos(t * (Math.PI / 2)) + 1;
}, },
easeOutSine: function(t) { easeOutSine: function(t: number): number {
return Math.sin(t * (Math.PI / 2)); return Math.sin(t * (Math.PI / 2));
}, },
easeInOutSine: function(t) { easeInOutSine: function(t: number): number {
return -(Math.cos(Math.PI * t) - 1) / 2; return -(Math.cos(Math.PI * t) - 1) / 2;
}, },
easeInExpo: function(t) { easeInExpo: function(t: number): number {
return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1)); return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1));
}, },
easeOutExpo: function(t) { easeOutExpo: function(t: number): number {
return (t === 1) ? 1 : (-Math.pow(2, -10 * t) + 1); return (t === 1) ? 1 : (-Math.pow(2, -10 * t) + 1);
}, },
easeInOutExpo: function(t) { easeInOutExpo: function(t: number): number {
if (t === 0) { if (t === 0) {
return 0; return 0;
} }
@ -106,14 +106,14 @@ var defaults = {
} }
return (-Math.pow(2, -10 * (t - 1)) + 2) / 2; return (-Math.pow(2, -10 * (t - 1)) + 2) / 2;
}, },
easeInCirc: function(t) { easeInCirc: function(t: number): number {
return -(Math.sqrt(1 - t * t) - 1); return -(Math.sqrt(1 - t * t) - 1);
}, },
easeOutCirc: function(t) { easeOutCirc: function(t: number): number {
t -= 1; t -= 1;
return Math.sqrt(1 - t * t); return Math.sqrt(1 - t * t);
}, },
easeInOutCirc: function(t) { easeInOutCirc: function(t: number): number {
t *= 2; t *= 2;
if (t < 1) { if (t < 1) {
return -(Math.sqrt(1 - t * t) - 1) / 2; return -(Math.sqrt(1 - t * t) - 1) / 2;
@ -121,7 +121,7 @@ var defaults = {
t -= 2; t -= 2;
return (Math.sqrt(1 - t * t) + 1) / 2; return (Math.sqrt(1 - t * t) + 1) / 2;
}, },
easeInElastic: function(t) { easeInElastic: function(t: number): number {
var s = 1.70158; var s = 1.70158;
var p = 0.3; var p = 0.3;
if (t === 0) { if (t === 0) {
@ -134,7 +134,7 @@ var defaults = {
t -= 1; t -= 1;
return -(Math.pow(2, 10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p)); return -(Math.pow(2, 10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
}, },
easeOutElastic: function(t) { easeOutElastic: function(t: number): number {
var s = 1.70158; var s = 1.70158;
var p = 0.3; var p = 0.3;
if (t === 0) { if (t === 0) {
@ -146,7 +146,7 @@ var defaults = {
var s = p / (2 * Math.PI) * Math.asin(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; return Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1;
}, },
easeInOutElastic: function(t) { easeInOutElastic: function(t: number): number {
var s = 1.70158; var s = 1.70158;
var p = 0.3 * 1.5; var p = 0.3 * 1.5;
if (t === 0) { if (t === 0) {
@ -164,16 +164,16 @@ var defaults = {
t -= 1; t -= 1;
return Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) / 2 + 1; return Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) / 2 + 1;
}, },
easeInBack: function(t) { easeInBack: function(t: number): number {
var s = 1.70158; var s = 1.70158;
return t * t * ((s + 1) * t - s); return t * t * ((s + 1) * t - s);
}, },
easeOutBack: function(t) { easeOutBack: function(t: number): number {
var s = 1.70158; var s = 1.70158;
t -= 1; t -= 1;
return (t * t * ((s + 1) * t + s) + 1); return (t * t * ((s + 1) * t + s) + 1);
}, },
easeInOutBack: function(t) { easeInOutBack: function(t: number): number {
var s = 1.70158 * 1.525; var s = 1.70158 * 1.525;
t *= 2; t *= 2;
if (t < 1) { if (t < 1) {
@ -182,10 +182,10 @@ var defaults = {
t -= 2; t -= 2;
return (t * t * ((s + 1) * t + s) + 2) / 2; return (t * t * ((s + 1) * t + s) + 2) / 2;
}, },
easeInBounce: function(t) { easeInBounce: function(t: number): number {
return 1 - this.easeOutBounce(1 - t); return 1 - this.easeOutBounce(1 - t);
}, },
easeOutBounce: function(t) { easeOutBounce: function(t: number): number {
if (t < (1 / 2.75)) { if (t < (1 / 2.75)) {
return 7.5625 * t * t; return 7.5625 * t * t;
} else if (t < (2 / 2.75)) { } else if (t < (2 / 2.75)) {
@ -199,7 +199,7 @@ var defaults = {
return 7.5625 * t * t + 0.984375; return 7.5625 * t * t + 0.984375;
} }
}, },
easeInOutBounce: function(t) { easeInOutBounce: function(t: number): number {
if (t < 0.5) { if (t < 0.5) {
return this.easeInBounce(t * 2) / 2; return this.easeInBounce(t * 2) / 2;
} }
@ -234,4 +234,6 @@ module.exports = {
return samples; return samples;
}, },
Defaults: defaults,
}; };

View File

@ -17,18 +17,20 @@ var RCTUIManager = require('NativeModules').UIManager;
var createStrictShapeTypeChecker = require('createStrictShapeTypeChecker'); var createStrictShapeTypeChecker = require('createStrictShapeTypeChecker');
var keyMirror = require('keyMirror'); var keyMirror = require('keyMirror');
var Types = keyMirror({ var TypesEnum = {
spring: true, spring: true,
linear: true, linear: true,
easeInEaseOut: true, easeInEaseOut: true,
easeIn: true, easeIn: true,
easeOut: true, easeOut: true,
}); };
var Types = keyMirror(TypesEnum);
var Properties = keyMirror({ var PropertiesEnum = {
opacity: true, opacity: true,
scaleXY: true, scaleXY: true,
}); };
var Properties = keyMirror(PropertiesEnum);
var animChecker = createStrictShapeTypeChecker({ var animChecker = createStrictShapeTypeChecker({
duration: PropTypes.number, duration: PropTypes.number,
@ -48,8 +50,8 @@ type Anim = {
delay?: number; delay?: number;
springDamping?: number; springDamping?: number;
initialVelocity?: number; initialVelocity?: number;
type?: $Enum<typeof Types>; type?: $Enum<typeof TypesEnum>;
property?: $Enum<typeof Properties>; property?: $Enum<typeof PropertiesEnum>;
} }
var configChecker = createStrictShapeTypeChecker({ var configChecker = createStrictShapeTypeChecker({

View File

@ -13,6 +13,7 @@
#import "RCTSparseArray.h" #import "RCTSparseArray.h"
#import "RCTUIManager.h" #import "RCTUIManager.h"
#import "RCTUtils.h"
#if CGFLOAT_IS_DOUBLE #if CGFLOAT_IS_DOUBLE
#define CG_APPEND(PREFIX, SUFFIX_F, SUFFIX_D) PREFIX##SUFFIX_D #define CG_APPEND(PREFIX, SUFFIX_F, SUFFIX_D) PREFIX##SUFFIX_D
@ -23,6 +24,8 @@
@implementation RCTAnimationExperimentalManager @implementation RCTAnimationExperimentalManager
{ {
RCTSparseArray *_animationRegistry; // Main thread only; animation tag -> view tag RCTSparseArray *_animationRegistry; // Main thread only; animation tag -> view tag
RCTSparseArray *_callbackRegistry; // Main thread only; animation tag -> callback
NSDictionary *_keypathMapping;
} }
RCT_EXPORT_MODULE() RCT_EXPORT_MODULE()
@ -33,6 +36,33 @@ RCT_EXPORT_MODULE()
{ {
if ((self = [super init])) { if ((self = [super init])) {
_animationRegistry = [[RCTSparseArray alloc] 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; return self;
@ -63,12 +93,25 @@ RCT_EXPORT_MODULE()
}; };
} }
RCT_EXPORT_METHOD(startAnimationForTag:(NSNumber *)reactTag 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 animationTag:(NSNumber *)animationTag
duration:(NSTimeInterval)duration duration:(NSTimeInterval)duration
delay:(NSTimeInterval)delay delay:(NSTimeInterval)delay
easingSample:(NSArray *)easingSample easingSample:(NSArray *)easingSample
properties:(NSDictionary *)properties) properties:(NSDictionary *)properties
callback:(RCTResponseSenderBlock)callback)
{ {
__weak RCTAnimationExperimentalManager *weakSelf = self; __weak RCTAnimationExperimentalManager *weakSelf = self;
[_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { [_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
@ -79,12 +122,21 @@ RCT_EXPORT_METHOD(startAnimationForTag:(NSNumber *)reactTag
RCTLogWarn(@"React tag #%@ is not registered with the view registry", reactTag); RCTLogWarn(@"React tag #%@ is not registered with the view registry", reactTag);
return; return;
} }
__block BOOL completionBlockSet = NO;
[properties enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) { [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; NSValue *toValue = nil;
if ([key isEqualToString:@"scaleXY"]) { if ([keypath isEqualToString:@"transform.scale"]) {
key = @"transform.scale"; CGPoint point = [RCTConvert CGPoint:obj];
toValue = obj[0]; if (point.x != point.y) {
return RCTInvalidAnimationProp(strongSelf->_callbackRegistry, animationTag, keypath, obj);
}
toValue = @(point.x);
} else if ([obj respondsToSelector:@selector(count)]) { } else if ([obj respondsToSelector:@selector(count)]) {
switch ([obj count]) { switch ([obj count]) {
case 2: case 2:
@ -100,11 +152,15 @@ RCT_EXPORT_METHOD(startAnimationForTag:(NSNumber *)reactTag
case 16: case 16:
toValue = [NSValue valueWithCGAffineTransform:[RCTConvert CGAffineTransform:obj]]; toValue = [NSValue valueWithCGAffineTransform:[RCTConvert CGAffineTransform:obj]];
break; 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;
} }
if (!toValue) toValue = obj;
const char *typeName = toValue.objCType; const char *typeName = toValue.objCType;
size_t count; size_t count;
@ -155,7 +211,7 @@ RCT_EXPORT_METHOD(startAnimationForTag:(NSNumber *)reactTag
break; break;
} }
NSValue *fromValue = [view.layer.presentationLayer valueForKeyPath:key]; NSValue *fromValue = [view.layer.presentationLayer valueForKeyPath:keypath];
CGFloat fromFields[count]; CGFloat fromFields[count];
[fromValue getValue:fromFields]; [fromValue getValue:fromFields];
@ -166,19 +222,32 @@ RCT_EXPORT_METHOD(startAnimationForTag:(NSNumber *)reactTag
CGFloat t = sample.CG_APPEND(, floatValue, doubleValue); CGFloat t = sample.CG_APPEND(, floatValue, doubleValue);
[sampledValues addObject:interpolationBlock(t)]; [sampledValues addObject:interpolationBlock(t)];
} }
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:keypath];
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:key];
animation.beginTime = CACurrentMediaTime() + delay; animation.beginTime = CACurrentMediaTime() + delay;
animation.duration = duration; animation.duration = duration;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animation.values = sampledValues; animation.values = sampledValues;
@try {
[view.layer setValue:toValue forKey:key]; [view.layer setValue:toValue forKey:keypath];
NSString *animationKey = [@"RCT" stringByAppendingString:RCTJSONStringify(@{@"tag": animationTag, @"key": keypath}, nil)];
NSString *animationKey = [NSString stringWithFormat:@"RCT.%@.%@", animationTag, key];
[view.layer addAnimation:animation forKey:animationKey]; [view.layer addAnimation:animation forKey:animationKey];
if (!completionBlockSet) {
strongSelf->_callbackRegistry[animationTag] = callback;
[CATransaction setCompletionBlock:^{
RCTResponseSenderBlock cb = strongSelf->_callbackRegistry[animationTag];
if (cb) {
cb(@[@YES]);
strongSelf->_callbackRegistry[animationTag] = nil;
}
}]; }];
completionBlockSet = YES;
}
}
@catch (NSException *exception) {
return RCTInvalidAnimationProp(strongSelf->_callbackRegistry, animationTag, keypath, toValue);
}
}
[CATransaction commit];
strongSelf->_animationRegistry[animationTag] = reactTag; strongSelf->_animationRegistry[animationTag] = reactTag;
}]; }];
} }
@ -194,19 +263,25 @@ RCT_EXPORT_METHOD(stopAnimation:(NSNumber *)animationTag)
UIView *view = viewRegistry[reactTag]; UIView *view = viewRegistry[reactTag];
for (NSString *animationKey in view.layer.animationKeys) { for (NSString *animationKey in view.layer.animationKeys) {
if ([animationKey hasPrefix:@"RCT"]) { if ([animationKey hasPrefix:@"RCT{"]) {
NSRange periodLocation = [animationKey rangeOfString:@"." options:0 range:(NSRange){3, animationKey.length - 3}]; NSDictionary *data = RCTJSONParse([animationKey substringFromIndex:3], nil);
if (periodLocation.location != NSNotFound) { if (animationTag.integerValue == [data[@"tag"] integerValue]) {
NSInteger integerTag = [[animationKey substringWithRange:(NSRange){3, periodLocation.location}] integerValue];
if (animationTag.integerValue == integerTag) {
[view.layer removeAnimationForKey:animationKey]; [view.layer removeAnimationForKey:animationKey];
} }
} }
} }
RCTResponseSenderBlock cb = strongSelf->_callbackRegistry[animationTag];
if (cb) {
cb(@[@NO]);
strongSelf->_callbackRegistry[animationTag] = nil;
} }
strongSelf->_animationRegistry[animationTag] = nil; strongSelf->_animationRegistry[animationTag] = nil;
}]; }];
} }
- (NSDictionary *)constantsToExport
{
return @{@"Properties": [_keypathMapping allKeys] };
}
@end @end

View File

@ -60,7 +60,7 @@ var TouchableBounce = React.createClass({
value: number, value: number,
velocity: number, velocity: number,
bounciness: number, bounciness: number,
fromValue?: ?Function | number, fromValue?: ?number,
callback?: ?Function callback?: ?Function
) { ) {
if (POPAnimation) { if (POPAnimation) {
@ -71,21 +71,21 @@ var TouchableBounce = React.createClass({
toValue: [value, value], toValue: [value, value],
velocity: [velocity, velocity], velocity: [velocity, velocity],
springBounciness: bounciness, springBounciness: bounciness,
fromValue: (undefined: ?any), fromValue: fromValue ? [fromValue, fromValue] : undefined,
}; };
if (fromValue) {
anim.fromValue = [fromValue, fromValue];
}
this.state.animationID = POPAnimation.createSpringAnimation(anim); this.state.animationID = POPAnimation.createSpringAnimation(anim);
this.addAnimation(this.state.animationID, callback); this.addAnimation(this.state.animationID, callback);
} else { } else {
AnimationExperimental.startAnimation(this, 300, 0, 'easeOutBack', {scaleXY: [value, value]}); AnimationExperimental.startAnimation(
if (fromValue && typeof fromValue === 'function') { {
callback = fromValue; node: this,
} duration: 300,
if (callback) { easing: 'easeOutBack',
setTimeout(callback, 300); property: 'scaleXY',
} toValue: { x: value, y: value},
},
callback
);
} }
}, },