Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for animating all properties using native driver #27506

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8c2e8cd
[ios] Added support for animating unsupported props with useNativeDriver
chrfalch Dec 13, 2019
d7d3957
[js] Added support for animating all props using native driver
chrfalch Dec 13, 2019
cc6911a
[android] Added support for animating all props with useNativeDriver
chrfalch Dec 13, 2019
7ce4990
Removed podfile changes from branch
chrfalch Dec 13, 2019
1dcc656
[android] Trying to fix failing android test.
chrfalch Dec 13, 2019
d2303d4
[ios] Fixed missing call to configure props in test setup.
chrfalch Dec 13, 2019
9f6bd56
[android] added calls to configureProps to get tests to work
chrfalch Dec 13, 2019
c183ceb
[android] fixed issue where property bags where swapped.
chrfalch Dec 13, 2019
c5597b5
[android] Fixed issue with prop bags again..
chrfalch Dec 15, 2019
5bcec89
Removed property configuration from Javascript side
chrfalch Dec 22, 2019
7ea0191
Removed configure props and renamed members
chrfalch Dec 22, 2019
550ecf6
Removed configure props
chrfalch Dec 22, 2019
ce3c4ec
Added affirmative test for all properties
chrfalch Dec 22, 2019
5972744
Added transform props to whitelist
chrfalch Dec 22, 2019
f08526e
Changed parameter for operation to be ReadableMap from WriteableMap
chrfalch Dec 22, 2019
ffebced
Added test to verify that the left property is not updated on the ui …
chrfalch Dec 22, 2019
345f716
Updated member names
chrfalch Dec 22, 2019
8f37658
Updated names
chrfalch Dec 22, 2019
6ac3631
Moved processing of native props queue to updateProps() function
chrfalch Dec 23, 2019
fa28aae
Inverted list of props to include only layoutProps
chrfalch Jan 5, 2020
5a459b8
Removed onLayout from layout props
chrfalch Jan 6, 2020
f4b7662
Added addEnqueuedUpdateProp method to be able to configure properties…
chrfalch Jan 6, 2020
f4395cf
Removed validateStyles
chrfalch Jan 7, 2020
064b763
Changed layoutProps to an NSSet
chrfalch Jan 7, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 11 additions & 42 deletions Libraries/Animated/src/NativeAnimatedHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import type {
import type {AnimationConfig, EndCallback} from './animations/Animation';
import type {InterpolationConfigType} from './nodes/AnimatedInterpolation';
import invariant from 'invariant';
import {colorToRgba} from './colorToRgba';

let __nativeAnimatedNodeTagCount = 1; /* used for animated nodes */
let __nativeAnimationIdCount = 1; /* used for started animations */
Expand Down Expand Up @@ -151,35 +152,10 @@ const API = {
animatedNodeTag,
);
},
};

/**
* 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).
*/
const STYLES_WHITELIST = {
opacity: true,
transform: true,
borderRadius: true,
borderBottomEndRadius: true,
borderBottomLeftRadius: true,
borderBottomRightRadius: true,
borderBottomStartRadius: true,
borderTopEndRadius: true,
borderTopLeftRadius: true,
borderTopRightRadius: true,
borderTopStartRadius: true,
elevation: true,
/* ios styles */
shadowOpacity: true,
shadowRadius: true,
/* legacy android transform properties */
scaleX: true,
scaleY: true,
translateX: true,
translateY: true,
addEnqueuedUpdateProp(prop: string) {
invariant(NativeAnimatedModule, 'Native animated module is not available');
NativeAnimatedModule.addEnqueuedUpdateProp(prop);
},
};

const TRANSFORM_WHITELIST = {
Expand All @@ -204,7 +180,11 @@ const SUPPORTED_INTERPOLATION_PARAMS = {
};

function addWhitelistedStyleProp(prop: string): void {
STYLES_WHITELIST[prop] = true;
console.warn(
'addWhitelistedStyleProp is now deprecated and no longer in use. ' +
'The native animated module now supports animating all properties ' +
'when the useNativeDriver flag is set to true.',
);
}

function addWhitelistedTransformProp(prop: string): void {
Expand Down Expand Up @@ -242,16 +222,6 @@ function validateTransform(
});
}

function validateStyles(styles: {[key: string]: ?number, ...}): void {
for (const key in styles) {
if (!STYLES_WHITELIST.hasOwnProperty(key)) {
throw new Error(
`Style property '${key}' is not supported by native animated module`,
);
}
}
}

function validateInterpolation(config: InterpolationConfigType): void {
for (const key in config) {
if (!SUPPORTED_INTERPOLATION_PARAMS.hasOwnProperty(key)) {
Expand Down Expand Up @@ -312,7 +282,7 @@ function transformDataType(value: number | string): number | string {
const radians = (degrees * Math.PI) / 180.0;
return radians;
} else {
return value;
return colorToRgba(value);
}
}

Expand All @@ -321,7 +291,6 @@ module.exports = {
addWhitelistedStyleProp,
addWhitelistedTransformProp,
addWhitelistedInterpolationParam,
validateStyles,
validateTransform,
validateInterpolation,
generateNewNodeTag,
Expand Down
1 change: 1 addition & 0 deletions Libraries/Animated/src/NativeAnimatedModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export interface Spec extends TurboModule {
eventName: string,
animatedNodeTag: number,
) => void;
+addEnqueuedUpdateProp: (prop: string) => void;

// Events
+addListener: (eventName: string) => void;
Expand Down
4 changes: 2 additions & 2 deletions Libraries/Animated/src/__tests__/AnimatedNative-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,7 @@ describe('Native Animated', () => {
}
});

it('fails for unsupported styles', () => {
it('allows all properties', () => {
const left = new Animated.Value(0);

TestRenderer.create(<Animated.View style={{left}} />);
Expand All @@ -652,7 +652,7 @@ describe('Native Animated', () => {
duration: 50,
useNativeDriver: true,
});
expect(animation.start).toThrowError(/left/);
expect(animation.start).not.toThrowError();
});
chrfalch marked this conversation as resolved.
Show resolved Hide resolved

it('works for any `static` props and styles', () => {
Expand Down
33 changes: 33 additions & 0 deletions Libraries/Animated/src/colorToRgba.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
chrfalch marked this conversation as resolved.
Show resolved Hide resolved
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/

/* eslint no-bitwise: 0 */

'use strict';

const normalizeColor = require('../../StyleSheet/normalizeColor');

function colorToRgba(input: string): string {
let int32Color = normalizeColor(input);
if (int32Color === null) {
return input;
}

int32Color = int32Color || 0;

const r = (int32Color & 0xff000000) >>> 24;
const g = (int32Color & 0x00ff0000) >>> 16;
const b = (int32Color & 0x0000ff00) >>> 8;
const a = (int32Color & 0x000000ff) / 255;

return `rgba(${r}, ${g}, ${b}, ${a})`;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's add some basic tests for this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see that you're just moving this, but if tests don't already exist, it'd be nice to add a few

export {colorToRgba};
21 changes: 2 additions & 19 deletions Libraries/Animated/src/nodes/AnimatedInterpolation.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,15 @@
* @format
*/

/* eslint no-bitwise: 0 */

'use strict';

import {colorToRgba} from '../colorToRgba';

const AnimatedNode = require('./AnimatedNode');
const AnimatedWithChildren = require('./AnimatedWithChildren');
const NativeAnimatedHelper = require('../NativeAnimatedHelper');

const invariant = require('invariant');
const normalizeColor = require('../../../StyleSheet/normalizeColor');

type ExtrapolateType = 'extend' | 'identity' | 'clamp';

Expand Down Expand Up @@ -168,22 +167,6 @@ function interpolate(
return result;
}

function colorToRgba(input: string): string {
let int32Color = normalizeColor(input);
if (int32Color === null) {
return input;
}

int32Color = int32Color || 0;

const r = (int32Color & 0xff000000) >>> 24;
const g = (int32Color & 0x00ff0000) >>> 16;
const b = (int32Color & 0x0000ff00) >>> 8;
const a = (int32Color & 0x000000ff) / 255;

return `rgba(${r}, ${g}, ${b}, ${a})`;
}

const stringShapeRegex = /[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/g;

/**
Expand Down
1 change: 0 additions & 1 deletion Libraries/Animated/src/nodes/AnimatedStyle.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ class AnimatedStyle extends AnimatedWithChildren {
// Non-animated styles are set using `setNativeProps`, no need
// to pass those as a part of the node config
}
NativeAnimatedHelper.validateStyles(styleConfig);
return {
type: 'style',
style: styleConfig,
Expand Down
23 changes: 9 additions & 14 deletions Libraries/NativeAnimation/Nodes/RCTInterpolationAnimatedNode.m
Original file line number Diff line number Diff line change
Expand Up @@ -133,32 +133,27 @@ - (void)performUpdate
if (_hasStringOutput) {
// 'rgba(0, 100, 200, 0)'
// ->
// 'rgba(${interpolations[0](input)}, ${interpolations[1](input)}, ...'
// numberic argb color
if (_numVals > 1) {
NSString *text = _soutputRange[0];
NSMutableString *formattedText = [NSMutableString stringWithString:text];
NSUInteger i = _numVals;
for (NSTextCheckingResult *match in [_matches reverseObjectEnumerator]) {
NSMutableArray<NSNumber*>* colors = [[NSMutableArray alloc] initWithCapacity:_numVals];
for (int i=0; i < _numVals; i++) {
CGFloat val = RCTInterpolateValueInRange(inputValue,
_inputRange,
_outputs[--i],
_outputs[i],
_extrapolateLeft,
_extrapolateRight);
NSString *str;
if (_shouldRound) {
// rgba requires that the r,g,b are integers.... so we want to round them, but we *dont* want to
// round the opacity (4th column).
bool isAlpha = i == 3;
CGFloat rounded = isAlpha ? round(val * 1000) / 1000 : round(val);
str = isAlpha ? [NSString stringWithFormat:@"%1.3f", rounded] : [NSString stringWithFormat:@"%1.0f", rounded];
CGFloat rounded = isAlpha ? round(val * 1000) / 1000 : round(val) / 255;
chrfalch marked this conversation as resolved.
Show resolved Hide resolved
[colors addObject:[NSNumber numberWithDouble:rounded]];
} else {
NSNumber *numberValue = [NSNumber numberWithDouble:val];
str = [numberValue stringValue];
NSNumber *numberValue = [NSNumber numberWithDouble:val / 255.0];
[colors addObject:numberValue];
}

[formattedText replaceCharactersInRange:[match range] withString:str];
}
self.animatedObject = formattedText;
self.animatedObject = colors;
} else {
self.animatedObject = [regex stringByReplacingMatchesInString:_soutputRange[0]
options:0
Expand Down
23 changes: 20 additions & 3 deletions Libraries/NativeAnimation/Nodes/RCTPropsAnimatedNode.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#import <React/RCTSurfacePresenterStub.h>
#import <React/RCTUIManager.h>

#import <React/RCTNativeAnimatedNodesManager.h>
#import <React/RCTAnimationUtils.h>
#import <React/RCTStyleAnimatedNode.h>
#import <React/RCTValueAnimatedNode.h>
Expand Down Expand Up @@ -67,9 +68,25 @@ - (void)updateView
[_bridge.surfacePresenter synchronouslyUpdateViewOnUIThread:_connectedViewTag
props:_propsDictionary];
} else {
[_bridge.uiManager synchronouslyUpdateViewOnUIThread:_connectedViewTag
viewName:_connectedViewName
props:_propsDictionary];
NSMutableDictionary *layoutProps = [NSMutableDictionary new];
NSMutableDictionary *props = [NSMutableDictionary new];

for (NSString *key in _propsDictionary.allKeys) {
if([self.manager.layoutProps containsObject: key]) {
[layoutProps setObject:_propsDictionary[key] forKey:key];
} else {
[props setObject:_propsDictionary[key] forKey:key];
}
}

if (props.count > 0) {
[_bridge.uiManager synchronouslyUpdateViewOnUIThread:_connectedViewTag
viewName:_connectedViewName
props:props];
}
if (layoutProps.count > 0) {
[self.manager enqueueUpdateViewOnUIManager:_connectedViewTag viewName:_connectedViewName props:layoutProps];
}
}
}

Expand Down
6 changes: 5 additions & 1 deletion Libraries/NativeAnimation/Nodes/RCTStyleAnimatedNode.m
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ - (void)performUpdate
if (node) {
if ([node isKindOfClass:[RCTValueAnimatedNode class]]) {
RCTValueAnimatedNode *parentNode = (RCTValueAnimatedNode *)node;
[self->_propsDictionary setObject:@(parentNode.value) forKey:property];
if(parentNode.animatedObject != nil) {
[self->_propsDictionary setObject:parentNode.animatedObject forKey:property];
} else {
[self->_propsDictionary setObject:@(parentNode.value) forKey:property];
}
} else if ([node isKindOfClass:[RCTTransformAnimatedNode class]]) {
RCTTransformAnimatedNode *parentNode = (RCTTransformAnimatedNode *)node;
[self->_propsDictionary addEntriesFromDictionary:parentNode.propsDictionary];
Expand Down
5 changes: 5 additions & 0 deletions Libraries/NativeAnimation/RCTNativeAnimatedModule.mm
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,11 @@ - (void)setBridge:(RCTBridge *)bridge
}];
}

RCT_EXPORT_METHOD(addEnqueuedUpdateProp:(NSString*)propName)
{
[_nodesManager addEnqueuedUpdateProp: propName];
}

#pragma mark -- Batch handling

- (void)addOperationBlock:(AnimatedOperation)operation
Expand Down
8 changes: 8 additions & 0 deletions Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,16 @@

@interface RCTNativeAnimatedNodesManager : NSObject

@property (nonatomic, copy, readonly) NSSet<NSString*>* layoutProps;

- (nonnull instancetype)initWithBridge:(nonnull RCTBridge *)bridge;

- (void)addEnqueuedUpdateProp:(NSString*)propName;

- (void)enqueueUpdateViewOnUIManager:(nonnull NSNumber *)reactTag
viewName:(NSString *) viewName
props:(NSMutableDictionary *)props;

- (void)updateAnimations;

- (void)stepAnimations:(nonnull CADisplayLink *)displaylink;
Expand Down
Loading