diff --git a/packages/react-native/Libraries/Animated/__tests__/Animated-itest.js b/packages/react-native/Libraries/Animated/__tests__/Animated-itest.js index d263ea0cbbc..a53ad8b8100 100644 --- a/packages/react-native/Libraries/Animated/__tests__/Animated-itest.js +++ b/packages/react-native/Libraries/Animated/__tests__/Animated-itest.js @@ -32,11 +32,9 @@ test('moving box by 100 points', () => { { width: 100, height: 100, - backgroundColor: 'red', }, {transform: [{translateX}]}, ]} - testID="box" /> ); } @@ -146,3 +144,56 @@ test('animation driven by onScroll event', () => { // TODO(T226364699): this should `toBe(100)` but we are not syncing shadow tree yet. expect(viewElement.getBoundingClientRect().y).toBe(0); }); + +test('animated opacity', () => { + let _opacity; + const viewRef = createRef(); + + function MyApp() { + const opacity = useAnimatedValue(1); + _opacity = opacity; + return ( + + ); + } + + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render(); + }); + + const viewElement = ensureInstance(viewRef.current, ReactNativeElement); + + expect(viewElement.getBoundingClientRect().x).toBe(0); + + Fantom.runTask(() => { + Animated.timing(_opacity, { + toValue: 0, + duration: 30, + useNativeDriver: true, + }).start(); + }); + + Fantom.unstable_produceFramesForDuration(30); + // $FlowFixMe[incompatible-use] + expect(Fantom.unstable_getDirectManipulationProps(viewElement).opacity).toBe( + 0, + ); + + // TODO: this shouldn't be neccessary but C++ Animated still schedules a React state update. + Fantom.runWorkLoop(); + + expect(root.getRenderedOutput({props: ['opacity']}).toJSX()).toEqual( + , + ); +}); diff --git a/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.cpp b/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.cpp index d3f8dc105a8..4c5eb9b66ea 100644 --- a/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.cpp +++ b/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.cpp @@ -756,6 +756,12 @@ bool NativeAnimatedNodesManager::commitProps() { if (fabricCommitCallback_ != nullptr) { if (!updateViewProps_.empty()) { + // Must call direct manipulation to set final values on components. + if (directManipulationCallback_ != nullptr) { + for (const auto& [viewTag, props] : updateViewProps_) { + directManipulationCallback_(viewTag, folly::dynamic(props)); + } + } fabricCommitCallback_(updateViewProps_); updateViewProps_.clear(); }