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

Optimize hot code-paths in uAS #3980

Merged
merged 3 commits into from
Feb 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
27 changes: 14 additions & 13 deletions src/reanimated2/hook/useAnimatedStyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ import NativeReanimatedModule from '../NativeReanimated';
import { useSharedValue } from './useSharedValue';
import {
buildWorkletsHash,
getStyleWithoutAnimations,
isAnimated,
styleDiff,
shallowEqual,
validateAnimatedStyles,
} from './utils';
import { DependencyList, Descriptor } from './commonTypes';
Expand All @@ -30,6 +29,7 @@ import {
BasicWorkletFunctionOptional,
NestedObjectValues,
SharedValue,
StyleProps,
} from '../commonTypes';
export interface AnimatedStyleResult {
viewDescriptors: ViewDescriptorsSet;
Expand Down Expand Up @@ -183,15 +183,19 @@ function styleUpdater(
const animations = state.animations ?? {};
const newValues = updater() ?? {};
const oldValues = state.last;
const nonAnimatedNewValues: StyleProps = {};

let hasAnimations = false;
let hasNonAnimatedValues = false;
for (const key in newValues) {
const value = newValues[key];
if (isAnimated(value)) {
prepareAnimation(value, animations[key], oldValues[key]);
animations[key] = value;
hasAnimations = true;
} else {
hasNonAnimatedValues = true;
nonAnimatedNewValues[key] = value;
delete animations[key];
}
}
Expand Down Expand Up @@ -244,21 +248,19 @@ function styleUpdater(
requestAnimationFrame(frame);
}
}
state.last = Object.assign({}, oldValues, newValues);
const style = getStyleWithoutAnimations(state.last);
if (style) {
updateProps(viewDescriptors, style, maybeViewRef);

if (hasNonAnimatedValues) {
updateProps(viewDescriptors, nonAnimatedNewValues, maybeViewRef);
}
} else {
state.isAnimationCancelled = true;
state.animations = [];

const diff = styleDiff(oldValues, newValues);
state.last = Object.assign({}, oldValues, newValues);
if (diff) {
if (!shallowEqual(oldValues, newValues)) {
updateProps(viewDescriptors, newValues, maybeViewRef);
}
}
state.last = newValues;
}

function jestStyleUpdater(
Expand Down Expand Up @@ -352,13 +354,12 @@ function jestStyleUpdater(
}

// calculate diff
const diff = styleDiff(oldValues, newValues);
state.last = Object.assign({}, oldValues, newValues);
state.last = newValues;

if (Object.keys(diff).length !== 0) {
if (!shallowEqual(oldValues, newValues)) {
updatePropsJestWrapper(
viewDescriptors,
diff,
newValues,
maybeViewRef,
animatedStyle,
adapters
Expand Down
47 changes: 12 additions & 35 deletions src/reanimated2/hook/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
Context,
NativeEvent,
NestedObjectValues,
StyleProps,
WorkletFunction,
AnimationObject,
} from '../commonTypes';
Expand Down Expand Up @@ -178,44 +177,22 @@ export function isAnimated(prop: NestedObjectValues<AnimationObject>): boolean {
return false;
}

export function styleDiff<T extends AnimatedStyle>(
oldStyle: AnimatedStyle,
newStyle: AnimatedStyle
): Partial<T> {
export function shallowEqual(a: any, b: any) {
'worklet';
const diff: any = {};
for (const key in oldStyle) {
if (newStyle[key] === undefined) {
diff[key] = null;
}
}
for (const key in newStyle) {
const value = newStyle[key];
const oldValue = oldStyle[key];

if (isAnimated(value)) {
// do nothing
continue;
}
if (oldValue !== value) {
diff[key] = value;
let aKeys = 0;
for (const key in a) {
aKeys += 1;
if (b[key] === a[key]) {
return false;
}
}
return diff;
}

export function getStyleWithoutAnimations(newStyle: AnimatedStyle): StyleProps {
'worklet';
const diff: StyleProps = {};

for (const key in newStyle) {
const value = newStyle[key];
if (isAnimated(value)) {
continue;
}
diff[key] = value;
// we use for loop here, as we want to avoid calling Object.keys that allocates new array
let bKeys = 0;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for (const key in b) {
bKeys += 1;
}
return diff;
return aKeys === bKeys;
}

export const validateAnimatedStyles = (styles: AnimatedStyle): void => {
Expand Down