From e79ccccceeb3982ac5606bf385a4c88dfddf90a9 Mon Sep 17 00:00:00 2001 From: Bartlomiej Bloniarz Date: Fri, 12 Dec 2025 08:00:34 -0800 Subject: [PATCH 1/5] Use dynamic for updates in Animated with Animation Backend Summary: For the testing purposes, during the development of Animation Backend, we changed the way props are modified from animated, to use the AnimatedProps. As these would currently not benefit c++ Animated, this diff reverts that to use `folly::dynamic` again. Differential Revision: D88743857 --- .../animated/NativeAnimatedNodesManager.cpp | 69 +++++++++---------- .../AnimatedPropsRegistry.cpp | 12 ++++ .../animationbackend/AnimatedPropsRegistry.h | 1 + .../animationbackend/AnimationBackend.cpp | 37 ++++------ .../animationbackend/AnimationBackend.h | 5 +- .../AnimationBackendCommitHook.cpp | 14 ++-- 6 files changed, 75 insertions(+), 63 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp index a17966d285d..e9a0309542d 100644 --- a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp @@ -1008,34 +1008,28 @@ AnimationMutations NativeAnimatedNodesManager::pullAnimationMutations() { } for (auto& [tag, props] : updateViewPropsDirect_) { - propsBuilder.storeDynamic(props); - mutations.push_back( - AnimationMutation{tag, nullptr, propsBuilder.get()}); + auto weakFamily = tagToShadowNodeFamily_[tag]; + + if (auto family = weakFamily.lock()) { + propsBuilder.storeDynamic(props); + mutations.batch.push_back( + AnimationMutation{ + .tag = tag, + .family = family, + .props = propsBuilder.get(), + }); + } containsChange = true; } { std::lock_guard lock(tagToShadowNodeFamilyMutex_); for (auto& [tag, props] : updateViewProps_) { - auto familyIt = tagToShadowNodeFamily_.find(tag); - if (familyIt == tagToShadowNodeFamily_.end()) { - continue; - } - if (auto family = familyIt->second.lock()) { - // C++ Animated produces props in the form of a folly::dynamic, so - // it wouldn't make sense to unpack it here. However, for the - // purposes of testing, we want to be able to use the statically - // typed AnimationMutation. At a later stage we will instead just - // pass the dynamic directly to propsBuilder and the new API could - // be used by 3rd party libraries or in the fututre by Animated. - if (props.find("width") != props.items().end()) { - propsBuilder.setWidth( - yoga::Style::SizeLength::points(props["width"].asDouble())); - } - if (props.find("height") != props.items().end()) { - propsBuilder.setHeight( - yoga::Style::SizeLength::points(props["height"].asDouble())); - } - mutations.push_back( + auto weakFamily = tagToShadowNodeFamily_[tag]; + + if (auto family = weakFamily.lock()) { + propsBuilder.storeDynamic(props); + mutations.hasLayoutUpdates = true; + mutations.batch.push_back( AnimationMutation{ .tag = tag, .family = family, @@ -1075,24 +1069,27 @@ AnimationMutations NativeAnimatedNodesManager::pullAnimationMutations() { isEventAnimationInProgress_ = false; for (auto& [tag, props] : updateViewPropsDirect_) { - propsBuilder.storeDynamic(props); - mutations.push_back( - AnimationMutation{ - .tag = tag, - .family = nullptr, - .props = propsBuilder.get(), - }); + auto weakFamily = tagToShadowNodeFamily_[tag]; + + if (auto family = weakFamily.lock()) { + propsBuilder.storeDynamic(props); + mutations.batch.push_back( + AnimationMutation{ + .tag = tag, + .family = family, + .props = propsBuilder.get(), + }); + } } { std::lock_guard lock(tagToShadowNodeFamilyMutex_); for (auto& [tag, props] : updateViewProps_) { - auto familyIt = tagToShadowNodeFamily_.find(tag); - if (familyIt == tagToShadowNodeFamily_.end()) { - continue; - } - if (auto family = familyIt->second.lock()) { + auto weakFamily = tagToShadowNodeFamily_[tag]; + + if (auto family = weakFamily.lock()) { propsBuilder.storeDynamic(props); - mutations.push_back( + mutations.hasLayoutUpdates = true; + mutations.batch.push_back( AnimationMutation{ .tag = tag, .family = family, diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.cpp b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.cpp index bfddaeaf427..6195af1a822 100644 --- a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.cpp @@ -35,6 +35,18 @@ void AnimatedPropsRegistry::update( auto& snapshot = it->second; auto& viewProps = snapshot->props; + if (animatedProps.rawProps) { + const auto& newRawProps = *animatedProps.rawProps; + auto& currentRawProps = snapshot->rawProps; + + if (currentRawProps) { + auto newRawPropsDynamic = newRawProps.toDynamic(); + currentRawProps->merge_patch(newRawPropsDynamic); + } else { + currentRawProps = + std::make_unique(newRawProps.toDynamic()); + } + } for (const auto& animatedProp : animatedProps.props) { snapshot->propNames.insert(animatedProp->propName); cloneProp(viewProps, *animatedProp); diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.h b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.h index 8df9fc03046..6991dc5199f 100644 --- a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.h +++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.h @@ -19,6 +19,7 @@ namespace facebook::react { struct PropsSnapshot { BaseViewProps props; std::unordered_set propNames; + std::unique_ptr rawProps; }; struct SurfaceContext { diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp index 6ee64331da2..e45417fdc99 100644 --- a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp @@ -6,6 +6,7 @@ */ #include "AnimationBackend.h" +#include #include #include #include @@ -49,18 +50,6 @@ static inline Props::Shared cloneProps( return newProps; } -static inline bool mutationHasLayoutUpdates( - facebook::react::AnimationMutation& mutation) { - for (auto& animatedProp : mutation.props.props) { - // TODO: there should also be a check for the dynamic part - if (animatedProp->propName == WIDTH || animatedProp->propName == HEIGHT || - animatedProp->propName == FLEX) { - return true; - } - } - return false; -} - AnimationBackend::AnimationBackend( StartOnRenderCallback&& startOnRenderCallback, StopOnRenderCallback&& stopOnRenderCallback, @@ -79,17 +68,19 @@ void AnimationBackend::onAnimationFrame(double timestamp) { std::unordered_map synchronousUpdates; std::unordered_map surfaceUpdates; - bool hasAnyLayoutUpdates = false; for (auto& callback : callbacks) { auto muatations = callback(static_cast(timestamp)); - for (auto& mutation : muatations) { - hasAnyLayoutUpdates |= mutationHasLayoutUpdates(mutation); - const auto family = mutation.family; - if (family != nullptr) { + if (muatations.hasLayoutUpdates) { + for (auto& mutation : muatations.batch) { + const auto family = mutation.family; + react_native_assert(family != nullptr); + auto& [families, updates] = surfaceUpdates[family->getSurfaceId()]; families.insert(family.get()); updates[mutation.tag] = std::move(mutation.props); - } else { + } + } else { + for (auto& mutation : muatations.batch) { synchronousUpdates[mutation.tag] = std::move(mutation.props); } } @@ -97,9 +88,11 @@ void AnimationBackend::onAnimationFrame(double timestamp) { animatedPropsRegistry_->update(surfaceUpdates); - if (hasAnyLayoutUpdates) { + if (!surfaceUpdates.empty()) { commitUpdates(surfaceUpdates); - } else { + } + + if (!synchronousUpdates.empty()) { synchronouslyUpdateProps(synchronousUpdates); } } @@ -165,8 +158,8 @@ void AnimationBackend::commitUpdates( void AnimationBackend::synchronouslyUpdateProps( const std::unordered_map& updates) { for (auto& [tag, animatedProps] : updates) { - // TODO: We shouldn't repack it into dynamic, but for that a rewrite of - // directManipulationCallback_ is needed + // TODO: We shouldn't repack it into dynamic, but for that a rewrite + // of directManipulationCallback_ is needed auto dyn = animationbackend::packAnimatedProps(animatedProps); directManipulationCallback_(tag, std::move(dyn)); } diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h index d8f7e3a326d..c39583cafee 100644 --- a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h +++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h @@ -38,7 +38,10 @@ struct AnimationMutation { AnimatedProps props; }; -using AnimationMutations = std::vector; +struct AnimationMutations { + std::vector batch; + bool hasLayoutUpdates{false}; +}; class AnimationBackend : public UIManagerAnimationBackend { public: diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackendCommitHook.cpp b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackendCommitHook.cpp index 5cb990fb68a..6d359ea8c1c 100644 --- a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackendCommitHook.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackendCommitHook.cpp @@ -43,13 +43,19 @@ RootShadowNode::Unshared AnimationBackendCommitHook::shadowTreeWillCommit( if (surfaceFamilies.contains(&shadowNode.getFamily()) && updates.contains(shadowNode.getTag())) { auto& snapshot = updates.at(shadowNode.getTag()); - if (!snapshot->propNames.empty()) { + if (!snapshot->propNames.empty() || snapshot->rawProps) { PropsParserContext propsParserContext{ shadowNode.getSurfaceId(), *shadowNode.getContextContainer()}; - - newProps = shadowNode.getComponentDescriptor().cloneProps( - propsParserContext, shadowNode.getProps(), {}); + if (snapshot->rawProps) { + newProps = shadowNode.getComponentDescriptor().cloneProps( + propsParserContext, + shadowNode.getProps(), + RawProps(*snapshot->rawProps)); + } else { + newProps = shadowNode.getComponentDescriptor().cloneProps( + propsParserContext, shadowNode.getProps(), {}); + } viewProps = std::const_pointer_cast( std::static_pointer_cast(newProps)); } From ad7c5e3ae44e5777fedb92ce55eae5ef852aaab4 Mon Sep 17 00:00:00 2001 From: Bartlomiej Bloniarz Date: Fri, 12 Dec 2025 08:00:34 -0800 Subject: [PATCH 2/5] Add test for width, height and opacity Summary: This diff simple extends the tests for AnimationBackend Differential Revision: D85753518 --- .../__tests__/AnimatedBackend-itest.js | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/packages/react-native/Libraries/Animated/__tests__/AnimatedBackend-itest.js b/packages/react-native/Libraries/Animated/__tests__/AnimatedBackend-itest.js index 0fd23272409..dbcc377052e 100644 --- a/packages/react-native/Libraries/Animated/__tests__/AnimatedBackend-itest.js +++ b/packages/react-native/Libraries/Animated/__tests__/AnimatedBackend-itest.js @@ -298,3 +298,72 @@ test('animate layout props and rerender in many components', () => { , ); }); + +test('animate width, height and opacity at once', () => { + const viewRef = createRef(); + allowStyleProp('width'); + allowStyleProp('height'); + + let _animatedWidth; + let _animatedHeight; + let _animatedOpacity; + let _parallelAnimation; + + function MyApp() { + const animatedWidth = useAnimatedValue(100); + const animatedHeight = useAnimatedValue(100); + const animatedOpacity = useAnimatedValue(1); + _animatedWidth = animatedWidth; + _animatedHeight = animatedHeight; + _animatedOpacity = animatedOpacity; + return ( + + ); + } + + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render(); + }); + + Fantom.runTask(() => { + _parallelAnimation = Animated.parallel([ + Animated.timing(_animatedWidth, { + toValue: 200, + duration: 100, + useNativeDriver: true, + }), + Animated.timing(_animatedHeight, { + toValue: 200, + duration: 100, + useNativeDriver: true, + }), + Animated.timing(_animatedOpacity, { + toValue: 0.5, + duration: 100, + useNativeDriver: true, + }), + ]).start(); + }); + + Fantom.unstable_produceFramesForDuration(100); + + // TODO: this shouldn't be neccessary since animation should be stopped after duration + Fantom.runTask(() => { + _parallelAnimation?.stop(); + }); + + expect( + root.getRenderedOutput({props: ['width', 'height', 'opacity']}).toJSX(), + ).toEqual(); +}); From 42ffeb17cf9c0522e2dac5880171c7e25952bc7f Mon Sep 17 00:00:00 2001 From: Bartlomiej Bloniarz Date: Fri, 12 Dec 2025 08:00:34 -0800 Subject: [PATCH 3/5] Add synchronous updates to the AnimatedPropsRegistry Summary: This diff changes the AnimatedPropsRegistry to also contain updates that go through the synchronous props update fast-path. Differential Revision: D89042934 --- .../__tests__/AnimatedBackend-itest.js | 115 +++++++++++++++++- .../animated/NativeAnimatedNodesManager.cpp | 60 ++++----- .../NativeAnimatedNodesManagerProvider.cpp | 46 +++---- .../AnimatedPropsRegistry.cpp | 7 ++ .../animationbackend/AnimatedPropsRegistry.h | 1 + .../animationbackend/AnimationBackend.cpp | 97 +++++++-------- .../animationbackend/AnimationBackend.h | 4 +- 7 files changed, 221 insertions(+), 109 deletions(-) diff --git a/packages/react-native/Libraries/Animated/__tests__/AnimatedBackend-itest.js b/packages/react-native/Libraries/Animated/__tests__/AnimatedBackend-itest.js index dbcc377052e..e36beafdfe3 100644 --- a/packages/react-native/Libraries/Animated/__tests__/AnimatedBackend-itest.js +++ b/packages/react-native/Libraries/Animated/__tests__/AnimatedBackend-itest.js @@ -191,15 +191,126 @@ test('animate layout props and rerender', () => { _setWidth(200); }); + // TODO: getFabricUpdateProps is not working with the cloneMutliple method + // expect(Fantom.unstable_getFabricUpdateProps(viewElement).height).toBe(50); + expect(root.getRenderedOutput({props: ['height', 'width']}).toJSX()).toEqual( + , + ); + + Fantom.unstable_produceFramesForDuration(500); + // TODO: this shouldn't be neccessary since animation should be stopped after duration Fantom.runTask(() => { _heightAnimation?.stop(); }); + expect(root.getRenderedOutput({props: ['height', 'width']}).toJSX()).toEqual( + , + ); + + Fantom.runTask(() => { + _setWidth(300); + }); + + expect(root.getRenderedOutput({props: ['height', 'width']}).toJSX()).toEqual( + , + ); +}); + +test('animate non-layout props and rerender', () => { + const viewRef = createRef(); + + let _animatedOpacity; + let _opacityAnimation; + let _setWidth; + + function MyApp() { + const animatedOpacity = useAnimatedValue(0); + const [width, setWidth] = useState(100); + _animatedOpacity = animatedOpacity; + _setWidth = setWidth; + return ( + + ); + } + + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render(); + }); + + const viewElement = ensureInstance(viewRef.current, ReactNativeElement); + + Fantom.runTask(() => { + _opacityAnimation = Animated.timing(_animatedOpacity, { + toValue: 0.5, + duration: 1000, + useNativeDriver: true, + }).start(); + }); + + Fantom.unstable_produceFramesForDuration(500); + + // TODO: rendered output should be at this point, but synchronous updates are not captured by fantom + expect(root.getRenderedOutput({props: ['width']}).toJSX()).toEqual( + , + ); + + expect( + Fantom.unstable_getDirectManipulationProps(viewElement).opacity, + ).toBeCloseTo(0.25, 0.001); + + // Re-render + Fantom.runTask(() => { + _setWidth(150); + }); + + expect(root.getRenderedOutput({props: ['opacity', 'width']}).toJSX()).toEqual( + , + ); + + Fantom.runTask(() => { + _setWidth(200); + }); + // TODO: getFabricUpdateProps is not working with the cloneMutliple method // expect(Fantom.unstable_getFabricUpdateProps(viewElement).height).toBe(50); - expect(root.getRenderedOutput({props: ['height', 'width']}).toJSX()).toEqual( - , + expect(root.getRenderedOutput({props: ['opacity', 'width']}).toJSX()).toEqual( + , + ); + + Fantom.unstable_produceFramesForDuration(500); + + // TODO: this shouldn't be neccessary since animation should be stopped after duration + Fantom.runTask(() => { + _opacityAnimation?.stop(); + }); + + // TODO: T246961305 rendered output should be at this point + expect(root.getRenderedOutput({props: ['width']}).toJSX()).toEqual( + , + ); + + expect(Fantom.unstable_getDirectManipulationProps(viewElement).opacity).toBe( + 0.5, + ); + + // Re-render + Fantom.runTask(() => { + _setWidth(300); + }); + + expect(root.getRenderedOutput({props: ['opacity', 'width']}).toJSX()).toEqual( + , ); }); diff --git a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp index e9a0309542d..545be89bd85 100644 --- a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp @@ -1007,33 +1007,33 @@ AnimationMutations NativeAnimatedNodesManager::pullAnimationMutations() { } } - for (auto& [tag, props] : updateViewPropsDirect_) { - auto weakFamily = tagToShadowNodeFamily_[tag]; - - if (auto family = weakFamily.lock()) { - propsBuilder.storeDynamic(props); - mutations.batch.push_back( - AnimationMutation{ - .tag = tag, - .family = family, - .props = propsBuilder.get(), - }); - } - containsChange = true; - } { std::lock_guard lock(tagToShadowNodeFamilyMutex_); + for (auto& [tag, props] : updateViewPropsDirect_) { + auto weakFamily = tagToShadowNodeFamily_[tag]; + + if (auto family = weakFamily.lock()) { + propsBuilder.storeDynamic(props); + mutations.batch.push_back( + AnimationMutation{ + .tag = tag, + .family = family, + .props = propsBuilder.get(), + }); + } + containsChange = true; + } for (auto& [tag, props] : updateViewProps_) { auto weakFamily = tagToShadowNodeFamily_[tag]; if (auto family = weakFamily.lock()) { propsBuilder.storeDynamic(props); - mutations.hasLayoutUpdates = true; mutations.batch.push_back( AnimationMutation{ .tag = tag, .family = family, .props = propsBuilder.get(), + .hasLayoutUpdates = true, }); } containsChange = true; @@ -1068,36 +1068,38 @@ AnimationMutations NativeAnimatedNodesManager::pullAnimationMutations() { isEventAnimationInProgress_ = false; - for (auto& [tag, props] : updateViewPropsDirect_) { - auto weakFamily = tagToShadowNodeFamily_[tag]; - - if (auto family = weakFamily.lock()) { - propsBuilder.storeDynamic(props); - mutations.batch.push_back( - AnimationMutation{ - .tag = tag, - .family = family, - .props = propsBuilder.get(), - }); - } - } { std::lock_guard lock(tagToShadowNodeFamilyMutex_); + for (auto& [tag, props] : updateViewPropsDirect_) { + auto weakFamily = tagToShadowNodeFamily_[tag]; + + if (auto family = weakFamily.lock()) { + propsBuilder.storeDynamic(props); + mutations.batch.push_back( + AnimationMutation{ + .tag = tag, + .family = family, + .props = propsBuilder.get(), + }); + } + } for (auto& [tag, props] : updateViewProps_) { auto weakFamily = tagToShadowNodeFamily_[tag]; if (auto family = weakFamily.lock()) { propsBuilder.storeDynamic(props); - mutations.hasLayoutUpdates = true; mutations.batch.push_back( AnimationMutation{ .tag = tag, .family = family, .props = propsBuilder.get(), + .hasLayoutUpdates = true, }); } } } + updateViewProps_.clear(); + updateViewPropsDirect_.clear(); } } else { // There is no active animation. Stop the render callback. diff --git a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManagerProvider.cpp b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManagerProvider.cpp index 569f662d975..bf8bad41ba9 100644 --- a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManagerProvider.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManagerProvider.cpp @@ -145,28 +145,30 @@ NativeAnimatedNodesManagerProvider::getOrCreate( uiManager->setNativeAnimatedDelegate(nativeAnimatedDelegate_); - animatedMountingOverrideDelegate_ = - std::make_shared( - *nativeAnimatedNodesManager_, *scheduler); - - // Register on existing surfaces - uiManager->getShadowTreeRegistry().enumerate( - [animatedMountingOverrideDelegate = - std::weak_ptr( - animatedMountingOverrideDelegate_)]( - const ShadowTree& shadowTree, bool& /*stop*/) { - shadowTree.getMountingCoordinator()->setMountingOverrideDelegate( - animatedMountingOverrideDelegate); - }); - // Register on surfaces started in the future - uiManager->setOnSurfaceStartCallback( - [animatedMountingOverrideDelegate = - std::weak_ptr( - animatedMountingOverrideDelegate_)]( - const ShadowTree& shadowTree) { - shadowTree.getMountingCoordinator()->setMountingOverrideDelegate( - animatedMountingOverrideDelegate); - }); + if (!ReactNativeFeatureFlags::useSharedAnimatedBackend()) { + animatedMountingOverrideDelegate_ = + std::make_shared( + *nativeAnimatedNodesManager_, *scheduler); + + // Register on existing surfaces + uiManager->getShadowTreeRegistry().enumerate( + [animatedMountingOverrideDelegate = + std::weak_ptr( + animatedMountingOverrideDelegate_)]( + const ShadowTree& shadowTree, bool& /*stop*/) { + shadowTree.getMountingCoordinator()->setMountingOverrideDelegate( + animatedMountingOverrideDelegate); + }); + // Register on surfaces started in the future + uiManager->setOnSurfaceStartCallback( + [animatedMountingOverrideDelegate = + std::weak_ptr( + animatedMountingOverrideDelegate_)]( + const ShadowTree& shadowTree) { + shadowTree.getMountingCoordinator()->setMountingOverrideDelegate( + animatedMountingOverrideDelegate); + }); + } } return nativeAnimatedNodesManager_; } diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.cpp b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.cpp index 6195af1a822..09ee3620c6f 100644 --- a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.cpp @@ -70,6 +70,13 @@ AnimatedPropsRegistry::getMap(SurfaceId surfaceId) { map.insert_or_assign(tag, std::move(propsSnapshot)); } else { auto& currentSnapshot = currentIt->second; + if (propsSnapshot->rawProps) { + if (currentSnapshot->rawProps) { + currentSnapshot->rawProps->merge_patch(*propsSnapshot->rawProps); + } else { + currentSnapshot->rawProps = std::move(propsSnapshot->rawProps); + } + } for (auto& propName : propsSnapshot->propNames) { currentSnapshot->propNames.insert(propName); updateProp(propName, currentSnapshot->props, *propsSnapshot); diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.h b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.h index 6991dc5199f..da3cbbb8484 100644 --- a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.h +++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.h @@ -30,6 +30,7 @@ struct SurfaceContext { struct SurfaceUpdates { std::unordered_set families; std::unordered_map propsMap; + bool hasLayoutUpdates{false}; }; using SnapshotMap = std::unordered_map>; diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp index e45417fdc99..37c79ef9096 100644 --- a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp @@ -65,35 +65,30 @@ AnimationBackend::AnimationBackend( commitHook_(uiManager, animatedPropsRegistry_) {} void AnimationBackend::onAnimationFrame(double timestamp) { - std::unordered_map synchronousUpdates; std::unordered_map surfaceUpdates; for (auto& callback : callbacks) { auto muatations = callback(static_cast(timestamp)); - if (muatations.hasLayoutUpdates) { - for (auto& mutation : muatations.batch) { - const auto family = mutation.family; - react_native_assert(family != nullptr); - - auto& [families, updates] = surfaceUpdates[family->getSurfaceId()]; - families.insert(family.get()); - updates[mutation.tag] = std::move(mutation.props); - } - } else { - for (auto& mutation : muatations.batch) { - synchronousUpdates[mutation.tag] = std::move(mutation.props); - } + for (auto& mutation : muatations.batch) { + const auto family = mutation.family; + react_native_assert(family != nullptr); + + auto& [families, updates, hasLayoutUpdates] = + surfaceUpdates[family->getSurfaceId()]; + hasLayoutUpdates |= mutation.hasLayoutUpdates; + families.insert(family.get()); + updates[mutation.tag] = std::move(mutation.props); } } animatedPropsRegistry_->update(surfaceUpdates); - if (!surfaceUpdates.empty()) { - commitUpdates(surfaceUpdates); - } - - if (!synchronousUpdates.empty()) { - synchronouslyUpdateProps(synchronousUpdates); + for (auto& [surfaceId, surfaceUpdates] : surfaceUpdates) { + if (surfaceUpdates.hasLayoutUpdates) { + commitUpdates(surfaceId, surfaceUpdates); + } else { + synchronouslyUpdateProps(surfaceUpdates.propsMap); + } } } @@ -119,40 +114,34 @@ void AnimationBackend::stop(bool isAsync) { } void AnimationBackend::commitUpdates( - std::unordered_map& surfaceUpdates) { - for (auto& surfaceEntry : surfaceUpdates) { - const auto& surfaceId = surfaceEntry.first; - const auto& surfaceFamilies = surfaceEntry.second.families; - auto& updates = surfaceEntry.second.propsMap; - uiManager_->getShadowTreeRegistry().visit( - surfaceId, [&surfaceFamilies, &updates](const ShadowTree& shadowTree) { - shadowTree.commit( - [&surfaceFamilies, - &updates](const RootShadowNode& oldRootShadowNode) { - return std::static_pointer_cast( - oldRootShadowNode.cloneMultiple( - surfaceFamilies, - [&surfaceFamilies, &updates]( - const ShadowNode& shadowNode, - const ShadowNodeFragment& fragment) { - auto newProps = - ShadowNodeFragment::propsPlaceholder(); - if (surfaceFamilies.contains( - &shadowNode.getFamily())) { - auto& animatedProps = - updates.at(shadowNode.getTag()); - newProps = cloneProps(animatedProps, shadowNode); - } - return shadowNode.clone( - {.props = newProps, - .children = fragment.children, - .state = shadowNode.getState(), - .runtimeShadowNodeReference = false}); - })); - }, - {.mountSynchronously = true}); - }); - } + SurfaceId surfaceId, + SurfaceUpdates& surfaceUpdates) { + auto& [surfaceFamilies, updates, hasLayoutUpdates] = surfaceUpdates; + uiManager_->getShadowTreeRegistry().visit( + surfaceId, [&surfaceFamilies, &updates](const ShadowTree& shadowTree) { + shadowTree.commit( + [&surfaceFamilies, + &updates](const RootShadowNode& oldRootShadowNode) { + return std::static_pointer_cast( + oldRootShadowNode.cloneMultiple( + surfaceFamilies, + [&surfaceFamilies, &updates]( + const ShadowNode& shadowNode, + const ShadowNodeFragment& fragment) { + auto newProps = ShadowNodeFragment::propsPlaceholder(); + if (surfaceFamilies.contains(&shadowNode.getFamily())) { + auto& animatedProps = updates.at(shadowNode.getTag()); + newProps = cloneProps(animatedProps, shadowNode); + } + return shadowNode.clone( + {.props = newProps, + .children = fragment.children, + .state = shadowNode.getState(), + .runtimeShadowNodeReference = false}); + })); + }, + {.mountSynchronously = true}); + }); } void AnimationBackend::synchronouslyUpdateProps( diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h index c39583cafee..7e0bd9bbe81 100644 --- a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h +++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h @@ -36,11 +36,11 @@ struct AnimationMutation { Tag tag; std::shared_ptr family; AnimatedProps props; + bool hasLayoutUpdates{false}; }; struct AnimationMutations { std::vector batch; - bool hasLayoutUpdates{false}; }; class AnimationBackend : public UIManagerAnimationBackend { @@ -66,7 +66,7 @@ class AnimationBackend : public UIManagerAnimationBackend { DirectManipulationCallback &&directManipulationCallback, FabricCommitCallback &&fabricCommitCallback, UIManager *uiManager); - void commitUpdates(std::unordered_map &surfaceUpdates); + void commitUpdates(SurfaceId surfaceId, SurfaceUpdates &surfaceUpdates); void synchronouslyUpdateProps(const std::unordered_map &updates); void clearRegistry(SurfaceId surfaceId) override; From d25da5a68f0b3838da0d70499aeaa841b70b3186 Mon Sep 17 00:00:00 2001 From: Bartlomiej Bloniarz Date: Fri, 12 Dec 2025 08:00:34 -0800 Subject: [PATCH 4/5] Sync on js thread after animation finished Summary: This diff adds synchronizing props to react, through a scheduled commit on the js thread. This is used by animated at the end of the animation, and leverages RSNRU to actually push the update to reactjs. Differential Revision: D89042949 --- .../__tests__/AnimatedBackend-itest.js | 10 ------- .../animated/NativeAnimatedNodesManager.cpp | 7 ++++- .../animated/NativeAnimatedNodesManager.h | 1 + .../NativeAnimatedNodesManagerProvider.cpp | 3 ++- .../animationbackend/AnimationBackend.cpp | 26 ++++++++++++++++--- .../animationbackend/AnimationBackend.h | 7 ++++- 6 files changed, 38 insertions(+), 16 deletions(-) diff --git a/packages/react-native/Libraries/Animated/__tests__/AnimatedBackend-itest.js b/packages/react-native/Libraries/Animated/__tests__/AnimatedBackend-itest.js index e36beafdfe3..3886f2ac320 100644 --- a/packages/react-native/Libraries/Animated/__tests__/AnimatedBackend-itest.js +++ b/packages/react-native/Libraries/Animated/__tests__/AnimatedBackend-itest.js @@ -70,16 +70,6 @@ test('animated opacity', () => { _opacityAnimation?.stop(); }); - // TODO: T246961305 rendered output should be at this point - expect(root.getRenderedOutput({props: ['opacity']}).toJSX()).toEqual( - , - ); - - // Re-render - Fantom.runTask(() => { - root.render(); - }); - expect(root.getRenderedOutput({props: ['opacity']}).toJSX()).toEqual( , ); diff --git a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp index 545be89bd85..e2dd8bcd751 100644 --- a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp @@ -909,6 +909,9 @@ void NativeAnimatedNodesManager::schedulePropsCommit( bool layoutStyleUpdated, bool forceFabricCommit) noexcept { if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) { + if (forceFabricCommit) { + shouldRequestAsyncFlush_ = true; + } if (layoutStyleUpdated) { mergeObjects(updateViewProps_[viewTag], props); } else { @@ -962,7 +965,7 @@ AnimationMutations NativeAnimatedNodesManager::pullAnimationMutations() { task(); } - AnimationMutations mutations; + AnimationMutations mutations{}; // Step through the animation loop if (isAnimationUpdateNeeded()) { @@ -1105,6 +1108,8 @@ AnimationMutations NativeAnimatedNodesManager::pullAnimationMutations() { // There is no active animation. Stop the render callback. stopRenderCallbackIfNeeded(false); } + mutations.shouldRequestAsyncFlush = shouldRequestAsyncFlush_; + shouldRequestAsyncFlush_ = false; return mutations; } #endif diff --git a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.h b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.h index 40632cbc1dc..daa007581fa 100644 --- a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.h +++ b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.h @@ -260,6 +260,7 @@ class NativeAnimatedNodesManager { std::unordered_map updateViewProps_{}; std::unordered_map updateViewPropsDirect_{}; + bool shouldRequestAsyncFlush_{false}; mutable std::mutex tagToShadowNodeFamilyMutex_; std::unordered_map> tagToShadowNodeFamily_{}; diff --git a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManagerProvider.cpp b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManagerProvider.cpp index bf8bad41ba9..b0d08ce645f 100644 --- a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManagerProvider.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManagerProvider.cpp @@ -95,7 +95,8 @@ NativeAnimatedNodesManagerProvider::getOrCreate( std::move(stopOnRenderCallback_), std::move(directManipulationCallback), std::move(fabricCommitCallback), - uiManager); + uiManager, + jsInvoker); nativeAnimatedNodesManager_ = std::make_shared(animationBackend_); diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp index 37c79ef9096..30867e68be4 100644 --- a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp @@ -55,21 +55,25 @@ AnimationBackend::AnimationBackend( StopOnRenderCallback&& stopOnRenderCallback, DirectManipulationCallback&& directManipulationCallback, FabricCommitCallback&& fabricCommitCallback, - UIManager* uiManager) + UIManager* uiManager, + std::shared_ptr jsInvoker) : startOnRenderCallback_(std::move(startOnRenderCallback)), stopOnRenderCallback_(std::move(stopOnRenderCallback)), directManipulationCallback_(std::move(directManipulationCallback)), fabricCommitCallback_(std::move(fabricCommitCallback)), animatedPropsRegistry_(std::make_shared()), uiManager_(uiManager), + jsInvoker_(std::move(jsInvoker)), commitHook_(uiManager, animatedPropsRegistry_) {} void AnimationBackend::onAnimationFrame(double timestamp) { std::unordered_map surfaceUpdates; + bool shouldRequestAsyncFlush = false; for (auto& callback : callbacks) { - auto muatations = callback(static_cast(timestamp)); - for (auto& mutation : muatations.batch) { + auto mutations = callback(static_cast(timestamp)); + shouldRequestAsyncFlush |= mutations.shouldRequestAsyncFlush; + for (auto& mutation : mutations.batch) { const auto family = mutation.family; react_native_assert(family != nullptr); @@ -90,6 +94,22 @@ void AnimationBackend::onAnimationFrame(double timestamp) { synchronouslyUpdateProps(surfaceUpdates.propsMap); } } + + if (shouldRequestAsyncFlush) { + // for (const auto& [surfaceId, _] : surfaceUpdates) { + jsInvoker_->invokeAsync([this]() { + uiManager_->getShadowTreeRegistry().enumerate( + [](const ShadowTree& shadowTree, bool& stop) { + shadowTree.commit( + [](const RootShadowNode& oldRootShadowNode) { + return std::static_pointer_cast( + oldRootShadowNode.ShadowNode::clone({})); + }, + {.source = ShadowTreeCommitSource::React}); + }); + }); + // } + } } void AnimationBackend::start(const Callback& callback, bool isAsync) { diff --git a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h index 7e0bd9bbe81..8b796d53c56 100644 --- a/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h +++ b/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h @@ -7,11 +7,13 @@ #pragma once +#include #include #include #include #include #include +#include #include #include "AnimatedProps.h" #include "AnimatedPropsBuilder.h" @@ -41,6 +43,7 @@ struct AnimationMutation { struct AnimationMutations { std::vector batch; + bool shouldRequestAsyncFlush{false}; }; class AnimationBackend : public UIManagerAnimationBackend { @@ -58,6 +61,7 @@ class AnimationBackend : public UIManagerAnimationBackend { const FabricCommitCallback fabricCommitCallback_; std::shared_ptr animatedPropsRegistry_; UIManager *uiManager_; + std::shared_ptr jsInvoker_; AnimationBackendCommitHook commitHook_; AnimationBackend( @@ -65,7 +69,8 @@ class AnimationBackend : public UIManagerAnimationBackend { StopOnRenderCallback &&stopOnRenderCallback, DirectManipulationCallback &&directManipulationCallback, FabricCommitCallback &&fabricCommitCallback, - UIManager *uiManager); + UIManager *uiManager, + std::shared_ptr jsInvoker); void commitUpdates(SurfaceId surfaceId, SurfaceUpdates &surfaceUpdates); void synchronouslyUpdateProps(const std::unordered_map &updates); void clearRegistry(SurfaceId surfaceId) override; From 98dd1c312da8fea0b55de13d5f37682ee27ac28b Mon Sep 17 00:00:00 2001 From: Bartlomiej Bloniarz Date: Fri, 12 Dec 2025 08:00:34 -0800 Subject: [PATCH 5/5] Move ShadowNodeFamily to PropsAnimatedNode Summary: This diff is a part of the process of getting the Animated-itest to work with Animation Backend. During testing I found that sometimes the disconnect method would cleanup `tagToShadowNodeFamily_` when there were more than one PropsAnimatedNode for a view, so we would wrongly skip an animation. By storing the family pointer on the props node we can avoid that. Differential Revision: D89042963 --- .../animated/NativeAnimatedNodesManager.cpp | 128 ++++++++---------- .../animated/NativeAnimatedNodesManager.h | 8 +- .../animated/nodes/PropsAnimatedNode.cpp | 32 ++++- .../animated/nodes/PropsAnimatedNode.h | 2 + 4 files changed, 94 insertions(+), 76 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp index e2dd8bcd751..386d6b5bd99 100644 --- a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp @@ -244,8 +244,7 @@ void NativeAnimatedNodesManager::connectAnimatedNodeToShadowNodeFamily( react_native_assert(propsNodeTag); auto node = getAnimatedNode(propsNodeTag); if (node != nullptr && family != nullptr) { - std::lock_guard lock(tagToShadowNodeFamilyMutex_); - tagToShadowNodeFamily_[family->getTag()] = family; + node->connectToShadowNodeFamily(family); } else { LOG(WARNING) << "Cannot ConnectAnimatedNodeToShadowNodeFamily, animated node has to be props type"; @@ -265,10 +264,6 @@ void NativeAnimatedNodesManager::disconnectAnimatedNodeFromView( std::lock_guard lock(connectedAnimatedNodesMutex_); connectedAnimatedNodes_.erase(viewTag); } - { - std::lock_guard lock(tagToShadowNodeFamilyMutex_); - tagToShadowNodeFamily_.erase(viewTag); - } updatedNodeTags_.insert(node->tag()); onManagedPropsRemoved(viewTag); @@ -907,16 +902,17 @@ void NativeAnimatedNodesManager::schedulePropsCommit( Tag viewTag, const folly::dynamic& props, bool layoutStyleUpdated, - bool forceFabricCommit) noexcept { + bool forceFabricCommit, + ShadowNodeFamily::Weak shadowNodeFamily) noexcept { if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) { if (forceFabricCommit) { shouldRequestAsyncFlush_ = true; } - if (layoutStyleUpdated) { - mergeObjects(updateViewProps_[viewTag], props); - } else { - mergeObjects(updateViewPropsDirect_[viewTag], props); - } + auto& current = layoutStyleUpdated + ? updateViewPropsForBackend_[viewTag] + : updateViewPropsDirectForBackend_[viewTag]; + current.first = std::move(shadowNodeFamily); + mergeObjects(current.second, props); return; } @@ -1010,38 +1006,36 @@ AnimationMutations NativeAnimatedNodesManager::pullAnimationMutations() { } } - { - std::lock_guard lock(tagToShadowNodeFamilyMutex_); - for (auto& [tag, props] : updateViewPropsDirect_) { - auto weakFamily = tagToShadowNodeFamily_[tag]; - - if (auto family = weakFamily.lock()) { - propsBuilder.storeDynamic(props); - mutations.batch.push_back( - AnimationMutation{ - .tag = tag, - .family = family, - .props = propsBuilder.get(), - }); - } - containsChange = true; + for (auto& [tag, update] : updateViewPropsDirectForBackend_) { + auto weakFamily = update.first; + + if (auto family = weakFamily.lock()) { + propsBuilder.storeDynamic(update.second); + mutations.batch.push_back( + AnimationMutation{ + .tag = tag, + .family = family, + .props = propsBuilder.get(), + }); } - for (auto& [tag, props] : updateViewProps_) { - auto weakFamily = tagToShadowNodeFamily_[tag]; - - if (auto family = weakFamily.lock()) { - propsBuilder.storeDynamic(props); - mutations.batch.push_back( - AnimationMutation{ - .tag = tag, - .family = family, - .props = propsBuilder.get(), - .hasLayoutUpdates = true, - }); - } - containsChange = true; + containsChange = true; + } + for (auto& [tag, update] : updateViewPropsForBackend_) { + auto weakFamily = update.first; + + if (auto family = weakFamily.lock()) { + propsBuilder.storeDynamic(update.second); + mutations.batch.push_back( + AnimationMutation{ + .tag = tag, + .family = family, + .props = propsBuilder.get(), + .hasLayoutUpdates = true, + }); } + containsChange = true; } + if (containsChange) { updateViewPropsDirect_.clear(); updateViewProps_.clear(); @@ -1071,36 +1065,34 @@ AnimationMutations NativeAnimatedNodesManager::pullAnimationMutations() { isEventAnimationInProgress_ = false; - { - std::lock_guard lock(tagToShadowNodeFamilyMutex_); - for (auto& [tag, props] : updateViewPropsDirect_) { - auto weakFamily = tagToShadowNodeFamily_[tag]; - - if (auto family = weakFamily.lock()) { - propsBuilder.storeDynamic(props); - mutations.batch.push_back( - AnimationMutation{ - .tag = tag, - .family = family, - .props = propsBuilder.get(), - }); - } + for (auto& [tag, update] : updateViewPropsDirectForBackend_) { + auto weakFamily = update.first; + + if (auto family = weakFamily.lock()) { + propsBuilder.storeDynamic(update.second); + mutations.batch.push_back( + AnimationMutation{ + .tag = tag, + .family = family, + .props = propsBuilder.get(), + }); } - for (auto& [tag, props] : updateViewProps_) { - auto weakFamily = tagToShadowNodeFamily_[tag]; - - if (auto family = weakFamily.lock()) { - propsBuilder.storeDynamic(props); - mutations.batch.push_back( - AnimationMutation{ - .tag = tag, - .family = family, - .props = propsBuilder.get(), - .hasLayoutUpdates = true, - }); - } + } + for (auto& [tag, update] : updateViewPropsForBackend_) { + auto weakFamily = update.first; + + if (auto family = weakFamily.lock()) { + propsBuilder.storeDynamic(update.second); + mutations.batch.push_back( + AnimationMutation{ + .tag = tag, + .family = family, + .props = propsBuilder.get(), + .hasLayoutUpdates = true, + }); } } + updateViewProps_.clear(); updateViewPropsDirect_.clear(); } diff --git a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.h b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.h index daa007581fa..50b0a7873e9 100644 --- a/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.h +++ b/packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.h @@ -153,7 +153,8 @@ class NativeAnimatedNodesManager { Tag viewTag, const folly::dynamic &props, bool layoutStyleUpdated, - bool forceFabricCommit) noexcept; + bool forceFabricCommit, + ShadowNodeFamily::Weak shadowNodeFamily = {}) noexcept; /** * Commits all pending animated property updates to their respective views. @@ -260,11 +261,10 @@ class NativeAnimatedNodesManager { std::unordered_map updateViewProps_{}; std::unordered_map updateViewPropsDirect_{}; + std::unordered_map> updateViewPropsForBackend_{}; + std::unordered_map> updateViewPropsDirectForBackend_{}; bool shouldRequestAsyncFlush_{false}; - mutable std::mutex tagToShadowNodeFamilyMutex_; - std::unordered_map> tagToShadowNodeFamily_{}; - /* * Sometimes a view is not longer connected to a PropsAnimatedNode, but * NativeAnimated has previously changed the view's props via direct diff --git a/packages/react-native/ReactCommon/react/renderer/animated/nodes/PropsAnimatedNode.cpp b/packages/react-native/ReactCommon/react/renderer/animated/nodes/PropsAnimatedNode.cpp index 48660d81531..5313a776151 100644 --- a/packages/react-native/ReactCommon/react/renderer/animated/nodes/PropsAnimatedNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animated/nodes/PropsAnimatedNode.cpp @@ -55,6 +55,11 @@ PropsAnimatedNode::PropsAnimatedNode( } } +void PropsAnimatedNode::connectToShadowNodeFamily( + ShadowNodeFamily::Weak shadowNodeFamily) { + shadowNodeFamily_ = std::move(shadowNodeFamily); +} + void PropsAnimatedNode::connectToView(Tag viewTag) { react_native_assert( connectedViewTag_ == animated::undefinedAnimatedNodeIdentifier && @@ -67,6 +72,7 @@ void PropsAnimatedNode::disconnectFromView(Tag viewTag) { connectedViewTag_ == viewTag && "Attempting to disconnect view that has not been connected with the given animated node."); connectedViewTag_ = animated::undefinedAnimatedNodeIdentifier; + shadowNodeFamily_.reset(); } // restore the value to whatever the value was on the ShadowNode instead of in @@ -74,8 +80,17 @@ void PropsAnimatedNode::disconnectFromView(Tag viewTag) { void PropsAnimatedNode::restoreDefaultValues() { // If node is already disconnected from View, we cannot restore default values if (connectedViewTag_ != animated::undefinedAnimatedNodeIdentifier) { - manager_->schedulePropsCommit( - connectedViewTag_, folly::dynamic::object(), false, false); + if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) { + manager_->schedulePropsCommit( + connectedViewTag_, + folly::dynamic::object(), + false, + false, + shadowNodeFamily_); + } else { + manager_->schedulePropsCommit( + connectedViewTag_, folly::dynamic::object(), false, false); + } } } @@ -147,8 +162,17 @@ void PropsAnimatedNode::update(bool forceFabricCommit) { layoutStyleUpdated_ = isLayoutStyleUpdated(getConfig()["props"], *manager_); - manager_->schedulePropsCommit( - connectedViewTag_, props_, layoutStyleUpdated_, forceFabricCommit); + if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) { + manager_->schedulePropsCommit( + connectedViewTag_, + props_, + layoutStyleUpdated_, + forceFabricCommit, + shadowNodeFamily_); + } else { + manager_->schedulePropsCommit( + connectedViewTag_, props_, layoutStyleUpdated_, forceFabricCommit); + } } } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/animated/nodes/PropsAnimatedNode.h b/packages/react-native/ReactCommon/react/renderer/animated/nodes/PropsAnimatedNode.h index 3da4db587d4..ee17db51246 100644 --- a/packages/react-native/ReactCommon/react/renderer/animated/nodes/PropsAnimatedNode.h +++ b/packages/react-native/ReactCommon/react/renderer/animated/nodes/PropsAnimatedNode.h @@ -21,6 +21,7 @@ namespace facebook::react { class PropsAnimatedNode final : public AnimatedNode { public: PropsAnimatedNode(Tag tag, const folly::dynamic &config, NativeAnimatedNodesManager &manager); + void connectToShadowNodeFamily(ShadowNodeFamily::Weak shadowNodeFamily); void connectToView(Tag viewTag); void disconnectFromView(Tag viewTag); void restoreDefaultValues(); @@ -51,6 +52,7 @@ class PropsAnimatedNode final : public AnimatedNode { bool layoutStyleUpdated_{false}; Tag connectedViewTag_{animated::undefinedAnimatedNodeIdentifier}; + ShadowNodeFamily::Weak shadowNodeFamily_; // Needed for PlatformColor resolver SurfaceId connectedRootTag_{animated::undefinedAnimatedNodeIdentifier};