Skip to content

Commit ac06f3b

Browse files
Bartlomiej Bloniarzmeta-codesync[bot]
authored andcommitted
Add option to trigger the backend from events (#54899)
Summary: Pull Request resolved: #54899 Currently the backend only applies changes when it is notified of a new animation frame. This setup doesn't work properly for events, as they could come in after the current frame was already calculated. So we need to allow events to trigger the backend manually. # Changelog [General] [Added] - `AnimationBackend::trigger` [General] [Changed] - Animated calls `AnimationBackend::trigger` to push updates from. events to the mounting layer Reviewed By: zeyap Differential Revision: D89286813 fbshipit-source-id: ffa83394bb620ad389b4888163e2206614171311
1 parent f9e94c0 commit ac06f3b

File tree

6 files changed

+82
-58
lines changed

6 files changed

+82
-58
lines changed

packages/react-native/Libraries/Animated/__tests__/Animated-itest.js

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7+
* @fantom_flags useSharedAnimatedBackend:*
78
* @flow strict-local
89
* @format
910
*/
@@ -13,6 +14,7 @@ import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';
1314
import type {HostInstance} from 'react-native';
1415

1516
import ensureInstance from '../../../src/private/__tests__/utilities/ensureInstance';
17+
import * as ReactNativeFeatureFlags from '../../../src/private/featureflags/ReactNativeFeatureFlags';
1618
import * as Fantom from '@react-native/fantom';
1719
import {createRef} from 'react';
1820
import {Animated, View, useAnimatedValue} from 'react-native';
@@ -564,12 +566,17 @@ test('animate layout props', () => {
564566
_heightAnimation?.stop();
565567
});
566568

567-
// $FlowFixMe[incompatible-use]
568-
expect(Fantom.unstable_getDirectManipulationProps(viewElement).height).toBe(
569-
100,
570-
);
569+
// animation backend does not push layut updates through the direct manipulation path
570+
// also it's changes are not currently reflected in the getFabricUpdateProps method, as
571+
// it only captures props that are updated through UIManager::updateShadowTree
572+
if (!ReactNativeFeatureFlags.useSharedAnimatedBackend()) {
573+
// $FlowFixMe[incompatible-use]
574+
expect(Fantom.unstable_getDirectManipulationProps(viewElement).height).toBe(
575+
100,
576+
);
571577

572-
expect(Fantom.unstable_getFabricUpdateProps(viewElement).height).toBe(100);
578+
expect(Fantom.unstable_getFabricUpdateProps(viewElement).height).toBe(100);
579+
}
573580

574581
expect(root.getRenderedOutput({props: ['height']}).toJSX()).toEqual(
575582
<rn-view height="100.000000" />,
@@ -634,7 +641,11 @@ test('AnimatedValue.interpolate', () => {
634641
),
635642
).toBe('[{"translateX":0.5},{"translateY":75}]');
636643
expect(viewElement.getBoundingClientRect().x).toBe(0.5);
637-
expect(viewElement.getBoundingClientRect().y).toBe(75);
644+
// TODO (T248792461) this doesn't work with animation backend, because the commit hook overrides the value from the second render.
645+
// The value displayed on the screen will be correct, but getBoundingClientRect will see the old shadow node
646+
if (!ReactNativeFeatureFlags.useSharedAnimatedBackend()) {
647+
expect(viewElement.getBoundingClientRect().y).toBe(75);
648+
}
638649
});
639650

640651
test('Animated.sequence', () => {

packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp

Lines changed: 57 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,15 @@ void NativeAnimatedNodesManager::handleAnimatedEvent(
517517
// That's why, in case this is called from the UI thread, we need to
518518
// proactivelly trigger the animation loop to avoid showing stale
519519
// frames.
520-
onRender();
520+
if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) {
521+
#ifdef RN_USE_ANIMATION_BACKEND
522+
if (auto animationBackend = animationBackend_.lock()) {
523+
animationBackend->trigger();
524+
}
525+
#endif
526+
} else {
527+
onRender();
528+
}
521529
}
522530
}
523531

@@ -968,6 +976,51 @@ void NativeAnimatedNodesManager::insertMutations(
968976
}
969977
}
970978

979+
AnimationMutations NativeAnimatedNodesManager::onAnimationFrameForBackend(
980+
AnimatedPropsBuilder& propsBuilder,
981+
double timestamp) {
982+
AnimationMutations mutations{};
983+
// Run all active animations
984+
auto hasFinishedAnimations = false;
985+
std::set<int> finishedAnimationValueNodes;
986+
for (const auto& [_id, driver] : activeAnimations_) {
987+
driver->runAnimationStep(timestamp);
988+
989+
if (driver->getIsComplete()) {
990+
hasFinishedAnimations = true;
991+
finishedAnimationValueNodes.insert(driver->getAnimatedValueTag());
992+
}
993+
}
994+
995+
// Update all animated nodes
996+
updateNodes(finishedAnimationValueNodes);
997+
998+
// remove finished animations
999+
if (hasFinishedAnimations) {
1000+
std::vector<int> finishedAnimations;
1001+
for (const auto& [animationId, driver] : activeAnimations_) {
1002+
if (driver->getIsComplete()) {
1003+
if (getAnimatedNode<ValueAnimatedNode>(driver->getAnimatedValueTag()) !=
1004+
nullptr) {
1005+
driver->stopAnimation();
1006+
}
1007+
finishedAnimations.emplace_back(animationId);
1008+
}
1009+
}
1010+
for (const auto& id : finishedAnimations) {
1011+
activeAnimations_.erase(id);
1012+
}
1013+
}
1014+
1015+
insertMutations(updateViewPropsDirectForBackend_, mutations, propsBuilder);
1016+
insertMutations(updateViewPropsForBackend_, mutations, propsBuilder, true);
1017+
1018+
updateViewPropsForBackend_.clear();
1019+
updateViewPropsDirectForBackend_.clear();
1020+
1021+
return mutations;
1022+
}
1023+
9711024
AnimationMutations NativeAnimatedNodesManager::pullAnimationMutations() {
9721025
if (!ReactNativeFeatureFlags::useSharedAnimatedBackend()) {
9731026
return {};
@@ -990,7 +1043,7 @@ AnimationMutations NativeAnimatedNodesManager::pullAnimationMutations() {
9901043
task();
9911044
}
9921045

993-
AnimationMutations mutations{};
1046+
AnimationMutations mutations;
9941047

9951048
// Step through the animation loop
9961049
if (isAnimationUpdateNeeded()) {
@@ -999,58 +1052,10 @@ AnimationMutations NativeAnimatedNodesManager::pullAnimationMutations() {
9991052
.count();
10001053

10011054
auto timestamp = static_cast<double>(microseconds) / 1000.0;
1002-
bool containsChange = false;
10031055
AnimatedPropsBuilder propsBuilder;
1004-
{
1005-
// copied from onAnimationFrame
1006-
// Run all active animations
1007-
auto hasFinishedAnimations = false;
1008-
std::set<int> finishedAnimationValueNodes;
1009-
for (const auto& [_id, driver] : activeAnimations_) {
1010-
driver->runAnimationStep(timestamp);
1056+
mutations = onAnimationFrameForBackend(propsBuilder, timestamp);
10111057

1012-
if (driver->getIsComplete()) {
1013-
hasFinishedAnimations = true;
1014-
finishedAnimationValueNodes.insert(driver->getAnimatedValueTag());
1015-
}
1016-
}
1017-
1018-
// Update all animated nodes
1019-
updateNodes(finishedAnimationValueNodes);
1020-
1021-
// remove finished animations
1022-
if (hasFinishedAnimations) {
1023-
std::vector<int> finishedAnimations;
1024-
for (const auto& [animationId, driver] : activeAnimations_) {
1025-
if (driver->getIsComplete()) {
1026-
if (getAnimatedNode<ValueAnimatedNode>(
1027-
driver->getAnimatedValueTag()) != nullptr) {
1028-
driver->stopAnimation();
1029-
}
1030-
finishedAnimations.emplace_back(animationId);
1031-
}
1032-
}
1033-
for (const auto& id : finishedAnimations) {
1034-
activeAnimations_.erase(id);
1035-
}
1036-
}
1037-
1038-
insertMutations(
1039-
updateViewPropsDirectForBackend_, mutations, propsBuilder);
1040-
1041-
insertMutations(
1042-
updateViewPropsForBackend_, mutations, propsBuilder, true);
1043-
1044-
containsChange = !updateViewPropsForBackend_.empty() ||
1045-
!updateViewPropsDirectForBackend_.empty();
1046-
1047-
if (containsChange) {
1048-
updateViewPropsDirectForBackend_.clear();
1049-
updateViewPropsForBackend_.clear();
1050-
}
1051-
}
1052-
1053-
if (!containsChange) {
1058+
if (mutations.batch.empty()) {
10541059
// The last animation tick didn't result in any changes to the UI.
10551060
// It is safe to assume any event animation that was in progress has
10561061
// completed.

packages/react-native/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ class NativeAnimatedNodesManager {
126126
AnimationMutations &mutations,
127127
AnimatedPropsBuilder &propsBuilder,
128128
bool hasLayoutUpdates = false);
129+
AnimationMutations onAnimationFrameForBackend(AnimatedPropsBuilder &propsBuilder, double timestamp);
129130
AnimationMutations pullAnimationMutations();
130131
#endif
131132

packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,11 @@ void AnimationBackend::stop(bool isAsync) {
129129
callbacks.clear();
130130
}
131131

132+
void AnimationBackend::trigger() {
133+
onAnimationFrame(
134+
std::chrono::steady_clock::now().time_since_epoch().count() / 1000);
135+
}
136+
132137
void AnimationBackend::commitUpdates(
133138
SurfaceId surfaceId,
134139
SurfaceUpdates& surfaceUpdates) {

packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ class AnimationBackend : public UIManagerAnimationBackend {
7878
void clearRegistry(SurfaceId surfaceId) override;
7979

8080
void onAnimationFrame(double timestamp) override;
81+
void trigger() override;
8182
void start(const Callback &callback, bool isAsync);
8283
void stop(bool isAsync) override;
8384
};

packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerAnimationBackend.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class UIManagerAnimationBackend {
2020
// TODO: T240293839 Move over start() function and mutation types
2121
virtual void stop(bool isAsync) = 0;
2222
virtual void clearRegistry(SurfaceId surfaceId) = 0;
23+
virtual void trigger() = 0;
2324
};
2425

2526
} // namespace facebook::react

0 commit comments

Comments
 (0)