Skip to content

Commit

Permalink
Drive any numerical prop via NativeAnimated
Browse files Browse the repository at this point in the history
Summary:
In theory, we should be able to animate any non-layout property, including custom ones. While there is still work to be done on the native side to fully enable this, we should start by dropping the prop whitelist.
Closes #10658

Differential Revision: D4379031

Pulled By: ericvicenti

fbshipit-source-id: fe9c30ea101e93a8b260d7d09a909fafbb82fee6
  • Loading branch information
ryangomba authored and facebook-github-bot committed Jan 27, 2017
1 parent 0bfb426 commit 7e869b9
Show file tree
Hide file tree
Showing 21 changed files with 271 additions and 199 deletions.
22 changes: 16 additions & 6 deletions Examples/UIExplorer/js/NativeAnimationsExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,11 @@ const {
Animated,
StyleSheet,
TouchableWithoutFeedback,
Slider,
} = ReactNative;

var AnimatedSlider = Animated.createAnimatedComponent(Slider);

class Tester extends React.Component {
state = {
native: new Animated.Value(0),
Expand Down Expand Up @@ -228,7 +231,6 @@ exports.description = 'Test out Native Animations';
exports.examples = [
{
title: 'Multistage With Multiply and rotation',
description: 'description',
render: function() {
return (
<Tester
Expand Down Expand Up @@ -278,7 +280,6 @@ exports.examples = [
},
{
title: 'Multistage With Multiply',
description: 'description',
render: function() {
return (
<Tester
Expand Down Expand Up @@ -322,7 +323,6 @@ exports.examples = [
},
{
title: 'Scale interpolation with clamping',
description: 'description',
render: function() {
return (
<Tester
Expand Down Expand Up @@ -352,7 +352,6 @@ exports.examples = [
},
{
title: 'Opacity without interpolation',
description: 'description',
render: function() {
return (
<Tester
Expand All @@ -374,7 +373,6 @@ exports.examples = [
},
{
title: 'Rotate interpolation',
description: 'description',
render: function() {
return (
<Tester
Expand Down Expand Up @@ -403,7 +401,6 @@ exports.examples = [
},
{
title: 'translateX => Animated.spring',
description: 'description',
render: function() {
return (
<Tester
Expand Down Expand Up @@ -454,6 +451,19 @@ exports.examples = [
</Tester>
);
},
},{
title: 'Drive custom property',
render: function() {
return (
<Tester
type="timing"
config={{ duration: 1000 }}>
{anim => (
<AnimatedSlider style={{}} value={anim} />
)}
</Tester>
);
},
},
{
title: 'Animated value listener',
Expand Down
1 change: 0 additions & 1 deletion Libraries/Animated/src/AnimatedImplementation.js
Original file line number Diff line number Diff line change
Expand Up @@ -1676,7 +1676,6 @@ class AnimatedProps extends Animated {
propsConfig[propKey] = value.__getNativeTag();
}
}
NativeAnimatedHelper.validateProps(propsConfig);
return {
type: 'props',
props: propsConfig,
Expand Down
33 changes: 10 additions & 23 deletions Libraries/Animated/src/NativeAnimatedHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,22 +100,19 @@ const API = {
};

/**
* Properties allowed by the native animated implementation.
* Styles allowed by the native animated implementation.
*
* In general native animated implementation should support any numeric property that doesn't need
* to be updated through the shadow view hierarchy (all non-layout properties). This list is limited
* to the properties that will perform best when animated off the JS thread.
* to be updated through the shadow view hierarchy (all non-layout properties).
*/
const PROPS_WHITELIST = {
style: {
opacity: true,
transform: true,
/* legacy android transform properties */
scaleX: true,
scaleY: true,
translateX: true,
translateY: true,
},
const STYLES_WHITELIST = {
opacity: true,
transform: true,
/* legacy android transform properties */
scaleX: true,
scaleY: true,
translateX: true,
translateY: true,
};

const TRANSFORM_WHITELIST = {
Expand All @@ -130,14 +127,6 @@ const TRANSFORM_WHITELIST = {
perspective: true,
};

function validateProps(params: Object): void {
for (var key in params) {
if (!PROPS_WHITELIST.hasOwnProperty(key)) {
throw new Error(`Property '${key}' is not supported by native animated module`);
}
}
}

function validateTransform(configs: Array<Object>): void {
configs.forEach((config) => {
if (!TRANSFORM_WHITELIST.hasOwnProperty(config.property)) {
Expand All @@ -147,7 +136,6 @@ function validateTransform(configs: Array<Object>): void {
}

function validateStyles(styles: Object): void {
var STYLES_WHITELIST = PROPS_WHITELIST.style || {};
for (var key in styles) {
if (!STYLES_WHITELIST.hasOwnProperty(key)) {
throw new Error(`Style property '${key}' is not supported by native animated module`);
Expand Down Expand Up @@ -188,7 +176,6 @@ function isNativeAnimatedAvailable(): boolean {

module.exports = {
API,
validateProps,
validateStyles,
validateTransform,
validateInterpolation,
Expand Down
7 changes: 3 additions & 4 deletions Libraries/NativeAnimation/Nodes/RCTPropsAnimatedNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@

@interface RCTPropsAnimatedNode : RCTAnimatedNode

@property (nonatomic, readonly) RCTViewPropertyMapper *propertyMapper;
- (void)connectToView:(NSNumber *)viewTag
viewName:(NSString *)viewName
uiManager:(RCTUIManager *)uiManager;

- (void)connectToView:(NSNumber *)viewTag uiManager:(RCTUIManager *)uiManager;
- (void)disconnectFromView:(NSNumber *)viewTag;

- (void)performViewUpdatesIfNecessary;

@end
41 changes: 28 additions & 13 deletions Libraries/NativeAnimation/Nodes/RCTPropsAnimatedNode.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,33 @@

#import "RCTPropsAnimatedNode.h"

#import <React/RCTLog.h>
#import <React/RCTUIManager.h>

#import "RCTAnimationUtils.h"
#import "RCTStyleAnimatedNode.h"
#import "RCTValueAnimatedNode.h"
#import "RCTViewPropertyMapper.h"

@implementation RCTPropsAnimatedNode

- (void)connectToView:(NSNumber *)viewTag uiManager:(RCTUIManager *)uiManager
{
_propertyMapper = [[RCTViewPropertyMapper alloc] initWithViewTag:viewTag uiManager:uiManager];
@implementation RCTPropsAnimatedNode {
NSNumber *_connectedViewTag;
NSString *_connectedViewName;
RCTUIManager *_uiManager;
}

- (void)disconnectFromView:(NSNumber *)viewTag
- (void)connectToView:(NSNumber *)viewTag
viewName:(NSString *)viewName
uiManager:(RCTUIManager *)uiManager
{
_propertyMapper = nil;
_connectedViewTag = viewTag;
_connectedViewName = viewName;
_uiManager = uiManager;
}

- (void)performUpdate
- (void)disconnectFromView:(NSNumber *)viewTag
{
[super performUpdate];
[self performViewUpdatesIfNecessary];
_connectedViewTag = nil;
_connectedViewName = nil;
_uiManager = nil;
}

- (NSString *)propertyNameForParentTag:(NSNumber *)parentTag
Expand All @@ -44,8 +50,15 @@ - (NSString *)propertyNameForParentTag:(NSNumber *)parentTag
return propertyName;
}

- (void)performViewUpdatesIfNecessary
- (void)performUpdate
{
[super performUpdate];

if (!_connectedViewTag) {
RCTLogError(@"Node has not been attached to a view");
return;
}

NSMutableDictionary *props = [NSMutableDictionary dictionary];
[self.parentNodes enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull parentTag, RCTAnimatedNode * _Nonnull parentNode, BOOL * _Nonnull stop) {

Expand All @@ -61,7 +74,9 @@ - (void)performViewUpdatesIfNecessary
}];

if (props.count) {
[_propertyMapper updateViewWithDictionary:props];
[_uiManager synchronouslyUpdateViewOnUIThread:_connectedViewTag
viewName:_connectedViewName
props:props];
}
}

Expand Down
41 changes: 6 additions & 35 deletions Libraries/NativeAnimation/Nodes/RCTTransformAnimatedNode.m
Original file line number Diff line number Diff line change
Expand Up @@ -33,56 +33,27 @@ - (void)performUpdate
{
[super performUpdate];

CATransform3D transform = CATransform3DIdentity;

NSArray<NSDictionary *> *transformConfigs = self.config[@"transforms"];
NSMutableArray<NSDictionary *> *transform = [NSMutableArray arrayWithCapacity:transformConfigs.count];
for (NSDictionary *transformConfig in transformConfigs) {
NSString *type = transformConfig[@"type"];
NSString *property = transformConfig[@"property"];

CGFloat value;
NSNumber *value;
if ([type isEqualToString: @"animated"]) {
NSNumber *nodeTag = transformConfig[@"nodeTag"];
RCTAnimatedNode *node = self.parentNodes[nodeTag];
if (![node isKindOfClass:[RCTValueAnimatedNode class]]) {
continue;
}
RCTValueAnimatedNode *parentNode = (RCTValueAnimatedNode *)node;
value = parentNode.value;
value = @(parentNode.value);
} else {
value = [transformConfig[@"value"] floatValue];
}

if ([property isEqualToString:@"scale"]) {
transform = CATransform3DScale(transform, value, value, 1);

} else if ([property isEqualToString:@"scaleX"]) {
transform = CATransform3DScale(transform, value, 1, 1);

} else if ([property isEqualToString:@"scaleY"]) {
transform = CATransform3DScale(transform, 1, value, 1);

} else if ([property isEqualToString:@"translateX"]) {
transform = CATransform3DTranslate(transform, value, 0, 0);

} else if ([property isEqualToString:@"translateY"]) {
transform = CATransform3DTranslate(transform, 0, value, 0);

} else if ([property isEqualToString:@"rotate"]) {
transform = CATransform3DRotate(transform, value, 0, 0, 1);

} else if ([property isEqualToString:@"rotateX"]) {
transform = CATransform3DRotate(transform, value, 1, 0, 0);

} else if ([property isEqualToString:@"rotateY"]) {
transform = CATransform3DRotate(transform, value, 0, 1, 0);

} else if ([property isEqualToString:@"perspective"]) {
transform.m34 = 1.0 / -value;
value = transformConfig[@"value"];
}
[transform addObject:@{property: value}];
}

_propsDictionary[@"transform"] = [NSValue valueWithCATransform3D:transform];
_propsDictionary[@"transform"] = transform;
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
/* Begin PBXBuildFile section */
13E501CC1D07A644005F35D8 /* RCTAnimationUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501B81D07A644005F35D8 /* RCTAnimationUtils.m */; };
13E501CF1D07A644005F35D8 /* RCTNativeAnimatedModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501BE1D07A644005F35D8 /* RCTNativeAnimatedModule.m */; };
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 */; };
13E501EB1D07A6C9005F35D8 /* RCTInterpolationAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501DD1D07A6C9005F35D8 /* RCTInterpolationAnimatedNode.m */; };
Expand All @@ -22,7 +21,6 @@
19F00F221DC8847500113FEE /* RCTEventAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 19F00F211DC8847500113FEE /* RCTEventAnimation.m */; };
19F00F231DC8848E00113FEE /* RCTEventAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 19F00F211DC8847500113FEE /* RCTEventAnimation.m */; };
2D3B5EF21D9B0B3100451313 /* RCTAnimationUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501B81D07A644005F35D8 /* RCTAnimationUtils.m */; };
2D3B5EF31D9B0B3400451313 /* RCTViewPropertyMapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501C81D07A644005F35D8 /* RCTViewPropertyMapper.m */; };
2D3B5EF41D9B0B3700451313 /* RCTNativeAnimatedModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501BE1D07A644005F35D8 /* RCTNativeAnimatedModule.m */; };
2D3B5EF51D9B0B4800451313 /* RCTDivisionAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C9894941D999639008027DB /* RCTDivisionAnimatedNode.m */; };
2D3B5EF61D9B0B4800451313 /* RCTDiffClampAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 193F64F31D776EC6004D1CAA /* RCTDiffClampAnimatedNode.m */; };
Expand Down Expand Up @@ -51,9 +49,7 @@
13E501B81D07A644005F35D8 /* RCTAnimationUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAnimationUtils.m; sourceTree = "<group>"; };
13E501BD1D07A644005F35D8 /* RCTNativeAnimatedModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTNativeAnimatedModule.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
13E501BE1D07A644005F35D8 /* RCTNativeAnimatedModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTNativeAnimatedModule.m; sourceTree = "<group>"; };
13E501C71D07A644005F35D8 /* RCTViewPropertyMapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTViewPropertyMapper.h; sourceTree = "<group>"; };
13E501C81D07A644005F35D8 /* RCTViewPropertyMapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTViewPropertyMapper.m; sourceTree = "<group>"; };
13E501D61D07A6C9005F35D8 /* RCTAdditionAnimatedNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTAdditionAnimatedNode.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
13E501D61D07A6C9005F35D8 /* RCTAdditionAnimatedNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAdditionAnimatedNode.h; sourceTree = "<group>"; };
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>"; };
Expand Down Expand Up @@ -130,8 +126,6 @@
children = (
13E501B71D07A644005F35D8 /* RCTAnimationUtils.h */,
13E501B81D07A644005F35D8 /* RCTAnimationUtils.m */,
13E501C71D07A644005F35D8 /* RCTViewPropertyMapper.h */,
13E501C81D07A644005F35D8 /* RCTViewPropertyMapper.m */,
13E501BD1D07A644005F35D8 /* RCTNativeAnimatedModule.h */,
13E501BE1D07A644005F35D8 /* RCTNativeAnimatedModule.m */,
94DA09161DC7971C00AEA8C9 /* RCTNativeAnimatedNodesManager.h */,
Expand Down Expand Up @@ -245,7 +239,6 @@
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 */,
9476E8EC1DC9232D005D5CD1 /* RCTNativeAnimatedNodesManager.m in Sources */,
Expand Down Expand Up @@ -273,7 +266,6 @@
13E501E81D07A6C9005F35D8 /* RCTAdditionAnimatedNode.m in Sources */,
5C9894951D999639008027DB /* RCTDivisionAnimatedNode.m in Sources */,
13E501EF1D07A6C9005F35D8 /* RCTTransformAnimatedNode.m in Sources */,
13E501D41D07A644005F35D8 /* RCTViewPropertyMapper.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
3 changes: 2 additions & 1 deletion Libraries/NativeAnimation/RCTNativeAnimatedModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,9 @@ - (void)setBridge:(RCTBridge *)bridge
RCT_EXPORT_METHOD(connectAnimatedNodeToView:(nonnull NSNumber *)nodeTag
viewTag:(nonnull NSNumber *)viewTag)
{
NSString *viewName = [self.bridge.uiManager viewNameForReactTag:viewTag];
[_operations addObject:^(RCTNativeAnimatedNodesManager *nodesManager) {
[nodesManager connectAnimatedNodeToView:nodeTag viewTag:viewTag];
[nodesManager connectAnimatedNodeToView:nodeTag viewTag:viewTag viewName:viewName];
}];
}

Expand Down
3 changes: 2 additions & 1 deletion Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
childTag:(nonnull NSNumber *)childTag;

- (void)connectAnimatedNodeToView:(nonnull NSNumber *)nodeTag
viewTag:(nonnull NSNumber *)viewTag;
viewTag:(nonnull NSNumber *)viewTag
viewName:(nonnull NSString *)viewName;

- (void)disconnectAnimatedNodeFromView:(nonnull NSNumber *)nodeTag
viewTag:(nonnull NSNumber *)viewTag;
Expand Down
Loading

0 comments on commit 7e869b9

Please sign in to comment.