Skip to content

Commit

Permalink
Modify AnimatedProps and AnimatedStyle to use AnimatedObject (#36741)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: #36741

AnimatedObject is a more generic version of AnimatedTransform, able to handle animated values within arrays and objects. This is useful for props of native components that may need to be animated per field.

This diff hooks up AnimatedObject to AnimatedProps and AnimatedStyle for values that are arrays or objects.

Changelog:
[Internal][Added] - Modify AnimatedProps and AnimatedStyle to use AnimatedObject

Reviewed By: rshest

Differential Revision: D44315336

fbshipit-source-id: 4d543550f24adbfc2ce4b15640df03f260da6106
  • Loading branch information
genkikondo authored and pull[bot] committed Feb 19, 2024
1 parent 34e2136 commit d4f2b03
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ describe('Animated tests', () => {

node.__attach();

expect(anim.__getChildren().length).toBe(3);
// Children: [AnimatedStyle, AnimatedTransform, AnimatedInterpolation, AnimatedObject, AnimatedObject]
expect(anim.__getChildren().length).toBe(5);

anim.setValue(0.5);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ describe('Animated tests', () => {

node.__attach();

expect(anim.__getChildren().length).toBe(3);
// Children: [AnimatedStyle, AnimatedTransform, AnimatedInterpolation, AnimatedObject, AnimatedObject]
expect(anim.__getChildren().length).toBe(5);

anim.setValue(0.5);

Expand Down
26 changes: 19 additions & 7 deletions packages/react-native/Libraries/Animated/nodes/AnimatedProps.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,35 @@ import {findNodeHandle} from '../../ReactNative/RendererProxy';
import {AnimatedEvent} from '../AnimatedEvent';
import NativeAnimatedHelper from '../NativeAnimatedHelper';
import AnimatedNode from './AnimatedNode';
import AnimatedObject, {hasAnimatedNode} from './AnimatedObject';
import AnimatedStyle from './AnimatedStyle';
import invariant from 'invariant';

function createAnimatedProps(inputProps: Object): Object {
const props: Object = {};
for (const key in inputProps) {
const value = inputProps[key];
if (key === 'style') {
props[key] = new AnimatedStyle(value);
} else if (value instanceof AnimatedNode) {
props[key] = value;
} else if (key !== 'children' && hasAnimatedNode(value)) {
props[key] = new AnimatedObject(value);
} else {
props[key] = value;
}
}
return props;
}

export default class AnimatedProps extends AnimatedNode {
_props: Object;
_animatedView: any;
_callback: () => void;

constructor(props: Object, callback: () => void) {
super();
if (props.style) {
props = {
...props,
style: new AnimatedStyle(props.style),
};
}
this._props = props;
this._props = createAnimatedProps(props);
this._callback = callback;
}

Expand Down
78 changes: 24 additions & 54 deletions packages/react-native/Libraries/Animated/nodes/AnimatedStyle.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@ import flattenStyle from '../../StyleSheet/flattenStyle';
import Platform from '../../Utilities/Platform';
import NativeAnimatedHelper from '../NativeAnimatedHelper';
import AnimatedNode from './AnimatedNode';
import AnimatedObject, {hasAnimatedNode} from './AnimatedObject';
import AnimatedTransform from './AnimatedTransform';
import AnimatedWithChildren from './AnimatedWithChildren';

function createAnimatedStyle(inputStyle: any): Object {
function createAnimatedStyle(
inputStyle: any,
keepUnanimatedValues: boolean,
): Object {
// $FlowFixMe[underconstrained-implicit-instantiation]
const style = flattenStyle(inputStyle);
const animatedStyles: any = {};
Expand All @@ -29,82 +33,48 @@ function createAnimatedStyle(inputStyle: any): Object {
animatedStyles[key] = new AnimatedTransform(value);
} else if (value instanceof AnimatedNode) {
animatedStyles[key] = value;
} else if (value && !Array.isArray(value) && typeof value === 'object') {
animatedStyles[key] = createAnimatedStyle(value);
} else if (hasAnimatedNode(value)) {
animatedStyles[key] = new AnimatedObject(value);
} else if (keepUnanimatedValues) {
animatedStyles[key] = value;
}
}
return animatedStyles;
}

function createStyleWithAnimatedTransform(inputStyle: any): Object {
// $FlowFixMe[underconstrained-implicit-instantiation]
let style = flattenStyle(inputStyle) || ({}: {[string]: any});

if (style.transform) {
style = {
...style,
transform: new AnimatedTransform(style.transform),
};
}
return style;
}

export default class AnimatedStyle extends AnimatedWithChildren {
_inputStyle: any;
_style: Object;

constructor(style: any) {
super();
if (Platform.OS === 'web') {
this._inputStyle = style;
this._style = createAnimatedStyle(style);
} else {
this._style = createStyleWithAnimatedTransform(style);
}
this._inputStyle = style;
this._style = createAnimatedStyle(style, Platform.OS !== 'web');
}

// Recursively get values for nested styles (like iOS's shadowOffset)
_walkStyleAndGetValues(style: any): {[string]: any | {...}} {
const updatedStyle: {[string]: any | {...}} = {};
for (const key in style) {
const value = style[key];
__getValue(): Object | Array<Object> {
const result: {[string]: any} = {};
for (const key in this._style) {
const value = this._style[key];
if (value instanceof AnimatedNode) {
updatedStyle[key] = value.__getValue();
} else if (value && !Array.isArray(value) && typeof value === 'object') {
// Support animating nested values (for example: shadowOffset.height)
updatedStyle[key] = this._walkStyleAndGetValues(value);
result[key] = value.__getValue();
} else {
updatedStyle[key] = value;
result[key] = value;
}
}
return updatedStyle;
}

__getValue(): Object | Array<Object> {
if (Platform.OS === 'web') {
return [this._inputStyle, this._walkStyleAndGetValues(this._style)];
}

return this._walkStyleAndGetValues(this._style);
return Platform.OS === 'web' ? [this._inputStyle, result] : result;
}

// Recursively get animated values for nested styles (like iOS's shadowOffset)
_walkStyleAndGetAnimatedValues(style: any): {[string]: any | {...}} {
const updatedStyle: {[string]: any | {...}} = {};
for (const key in style) {
const value = style[key];
__getAnimatedValue(): Object {
const result: {[string]: any} = {};
for (const key in this._style) {
const value = this._style[key];
if (value instanceof AnimatedNode) {
updatedStyle[key] = value.__getAnimatedValue();
} else if (value && !Array.isArray(value) && typeof value === 'object') {
// Support animating nested values (for example: shadowOffset.height)
updatedStyle[key] = this._walkStyleAndGetAnimatedValues(value);
result[key] = value.__getAnimatedValue();
}
}
return updatedStyle;
}

__getAnimatedValue(): Object {
return this._walkStyleAndGetAnimatedValues(this._style);
return result;
}

__attach(): void {
Expand Down

0 comments on commit d4f2b03

Please sign in to comment.