introduce Fantom.unstable_getDirectManipulationProps (#51814)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/51814

changelog: [internal]

introduce Fantom.unstable_getDirectManipulationProps to make it possible to inspect what is changed by C++ Animated module before animation finishes.

Reviewed By: zeyap, rubennorte

Differential Revision: D75816105

fbshipit-source-id: 681829ca9d3f77e57213e9f88f4f24b340a982ed
This commit is contained in:
Samuel Susla
2025-06-04 11:51:54 -07:00
committed by Facebook GitHub Bot
parent ed26358f63
commit 27f4801c82
3 changed files with 38 additions and 8 deletions
+18
View File
@@ -207,6 +207,24 @@ export function unstable_produceFramesForDuration(milliseconds: number) {
NativeFantom.produceFramesForDuration(milliseconds);
}
/**
* Returns props appplied via direct manipulation to a view represented by shadow node.
* Direct manipulation is used by C++ Animated to change view properties on UI tick
* while the animation is in progress. Once animation finishes, the final state is committed
* to the shadow tree and result is observable through other JavaScript APIs, like `measure`.
*
* @param node - The node for which to retrieve direct manipulation props.
* @returns Mixed type data containing the direct manipulation properties
*
* Note: This API is marked as unstable and may change in future versions.
*/
export function unstable_getDirectManipulationProps(
node: ReactNativeElement,
): mixed {
const shadowNode = getNativeNodeReference(node);
return NativeFantom.getDirectManipulationProps(shadowNode);
}
/**
* Simulates running a task on the UI thread and forces side effect to drain
* the event queue, scheduling events to be dispatched to JavaScript.
@@ -49,9 +49,7 @@ test('moving box by 100 points', () => {
const viewElement = ensureInstance(viewRef.current, ReactNativeElement);
let boundingClientRect = viewElement.getBoundingClientRect();
expect(boundingClientRect.x).toBe(0);
expect(viewElement.getBoundingClientRect().x).toBe(0);
Fantom.runTask(() => {
Animated.timing(_translateX, {
@@ -61,13 +59,26 @@ test('moving box by 100 points', () => {
}).start();
});
Fantom.unstable_produceFramesForDuration(1000);
boundingClientRect = viewElement.getBoundingClientRect();
expect(boundingClientRect.x).toBe(100);
Fantom.unstable_produceFramesForDuration(500);
// shadow tree is not synchronised yet, position X is still 0.
expect(viewElement.getBoundingClientRect().x).toBe(0);
const transform =
// $FlowFixMe[incompatible-use]
Fantom.unstable_getDirectManipulationProps(viewElement).transform[0];
// direct manipulation has been applied. 50% through the animation
// and with linear animation, that is position X = 50.
expect(transform.translateX).toBeCloseTo(50, 0.001);
Fantom.unstable_produceFramesForDuration(500);
// Animation is completed now. C++ Animated will commit the final position to the shadow tree.
expect(viewElement.getBoundingClientRect().x).toBe(100);
// TODO: this shouldn't be needed but C++ Animated still schedules a React state update
// for synchronisation, even though it doesn't need to.
Fantom.runWorkLoop();
boundingClientRect = viewElement.getBoundingClientRect();
expect(boundingClientRect.x).toBe(100);
expect(viewElement.getBoundingClientRect().x).toBe(100);
});
@@ -88,6 +88,7 @@ interface Spec extends TurboModule {
width: number,
) => void;
takeMountingManagerLogs: (surfaceId: number) => Array<string>;
getDirectManipulationProps: (shadowNode: mixed /* ShadowNode */) => mixed;
flushMessageQueue: () => void;
flushEventQueue: () => void;
produceFramesForDuration: (miliseconds: number) => void;