diff --git a/packages/react-native/Libraries/Animated/__tests__/Animated-itest.js b/packages/react-native/Libraries/Animated/__tests__/Animated-itest.js index 2a19a6a2d02..d712e9908ab 100644 --- a/packages/react-native/Libraries/Animated/__tests__/Animated-itest.js +++ b/packages/react-native/Libraries/Animated/__tests__/Animated-itest.js @@ -355,3 +355,91 @@ describe('Value.flattenOffset', () => { Fantom.runWorkLoop(); }); }); + +describe('Value.extractOffset', () => { + it('sets the offset value to the base value and resets the base value to zero', () => { + const scrollViewRef = createRef(); + const viewRef = createRef(); + let _onScroll; + + function PressableWithNativeDriver() { + _onScroll = useAnimatedValue(0); + + return ( + + + + + + + ); + } + + const root = Fantom.createRoot(); + Fantom.runTask(() => { + root.render(); + }); + + const scrollViewelement = ensureInstance( + scrollViewRef.current, + ReactNativeElement, + ); + const viewElement = ensureInstance(viewRef.current, ReactNativeElement); + + Fantom.scrollTo(scrollViewelement, { + x: 0, + y: 10, + }); + + Fantom.runTask(() => { + _onScroll.setOffset(15); + // Sets offset to be 15 + 10 = 25 (this is not observable from JS). + _onScroll.extractOffset(); + }); + + let transform = + // $FlowFixMe[incompatible-use] + Fantom.unstable_getDirectManipulationProps(viewElement).transform[0]; + + // Animated value is now 0 but offset is 25. The final value is 25. + expect(transform.translateY).toBeCloseTo(25, 0.001); + + Fantom.runTask(() => { + // Sets offset 35, overriding the previous `setOffset`. + // Due to `extractOffset`, base value was restarted to 0. + _onScroll.setOffset(35); + }); + + transform = + // $FlowFixMe[incompatible-use] + Fantom.unstable_getDirectManipulationProps(viewElement).transform[0]; + + // `extractOffset` resets value back to 0. + // Previously we set offset to 35. The final value is 35. + expect(transform.translateY).toBeCloseTo(35, 0.001); + + // TODO: this shouldn't be neccessary. + Fantom.runWorkLoop(); + }); +}); diff --git a/packages/react-native/ReactCxxPlatform/react/renderer/animated/AnimatedModule.cpp b/packages/react-native/ReactCxxPlatform/react/renderer/animated/AnimatedModule.cpp index b8eca745894..43f3fa3ec11 100644 --- a/packages/react-native/ReactCxxPlatform/react/renderer/animated/AnimatedModule.cpp +++ b/packages/react-native/ReactCxxPlatform/react/renderer/animated/AnimatedModule.cpp @@ -137,8 +137,8 @@ void AnimatedModule::flattenAnimatedNodeOffset( void AnimatedModule::extractAnimatedNodeOffset( jsi::Runtime& /*rt*/, - Tag /*nodeTag*/) { - // TODO(T196513004): missing implementation + Tag nodeTag) { + operations_.push_back(ExtractAnimatedNodeOffsetOp({.nodeTag = nodeTag})); } void AnimatedModule::connectAnimatedNodeToView( @@ -253,6 +253,8 @@ void AnimatedModule::executeOperation(const Operation& operation) { nodesManager_->setAnimatedNodeOffset(op.nodeTag, op.offset); } else if constexpr (std::is_same_v) { nodesManager_->flattenAnimatedNodeOffset(op.nodeTag); + } else if constexpr (std::is_same_v) { + nodesManager_->extractAnimatedNodeOffsetOp(op.nodeTag); } else if constexpr (std::is_same_v) { nodesManager_->connectAnimatedNodeToView(op.nodeTag, op.viewTag); } else if constexpr (std::is_same_v< diff --git a/packages/react-native/ReactCxxPlatform/react/renderer/animated/AnimatedModule.h b/packages/react-native/ReactCxxPlatform/react/renderer/animated/AnimatedModule.h index f915cb05a59..f7d72415d50 100644 --- a/packages/react-native/ReactCxxPlatform/react/renderer/animated/AnimatedModule.h +++ b/packages/react-native/ReactCxxPlatform/react/renderer/animated/AnimatedModule.h @@ -75,6 +75,10 @@ class AnimatedModule : public NativeAnimatedModuleCxxSpec, Tag nodeTag{}; }; + struct ExtractAnimatedNodeOffsetOp { + Tag nodeTag{}; + }; + struct ConnectAnimatedNodeToViewOp { Tag nodeTag{}; Tag viewTag{}; @@ -120,6 +124,7 @@ class AnimatedModule : public NativeAnimatedModuleCxxSpec, DisconnectAnimatedNodeFromViewOp, RestoreDefaultValuesOp, FlattenAnimatedNodeOffsetOp, + ExtractAnimatedNodeOffsetOp, DropAnimatedNodeOp, AddAnimatedEventToViewOp, RemoveAnimatedEventFromViewOp>; diff --git a/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.cpp b/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.cpp index bebbe64d3cf..53ebb05e07c 100644 --- a/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.cpp +++ b/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.cpp @@ -260,6 +260,12 @@ void NativeAnimatedNodesManager::flattenAnimatedNodeOffset(Tag tag) { } } +void NativeAnimatedNodesManager::extractAnimatedNodeOffsetOp(Tag tag) { + if (auto node = getAnimatedNode(tag)) { + node->extractOffset(); + } +} + void NativeAnimatedNodesManager::stopAnimationsForNode(Tag nodeTag) { std::vector discardedAnimIds{}; diff --git a/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.h b/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.h index a91290b5c4c..b19f9a83b43 100644 --- a/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.h +++ b/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.h @@ -98,6 +98,8 @@ class NativeAnimatedNodesManager { void flattenAnimatedNodeOffset(Tag tag); + void extractAnimatedNodeOffsetOp(Tag tag); + void setAnimatedNodeOffset(Tag tag, double offset); // drivers diff --git a/packages/react-native/ReactCxxPlatform/react/renderer/animated/nodes/ValueAnimatedNode.cpp b/packages/react-native/ReactCxxPlatform/react/renderer/animated/nodes/ValueAnimatedNode.cpp index 4a96d01fe4e..963f04f7822 100644 --- a/packages/react-native/ReactCxxPlatform/react/renderer/animated/nodes/ValueAnimatedNode.cpp +++ b/packages/react-native/ReactCxxPlatform/react/renderer/animated/nodes/ValueAnimatedNode.cpp @@ -71,8 +71,9 @@ void ValueAnimatedNode::flattenOffset() noexcept { } void ValueAnimatedNode::extractOffset() noexcept { - setOffset(value_ + getOffset()); - setRawValue(0.0f); + offset_ += value_; + value_ = 0; + onValueUpdate(); } void ValueAnimatedNode::onValueUpdate() noexcept {