Skip to content

Commit

Permalink
Fix layout animations flickering on Android [New Architecture] (#6518)
Browse files Browse the repository at this point in the history
## Summary

This PR fixes issues with layout animation flickering on Android. The
issue was caused by overwriting the `layoutAnimations_` on the JS
thread, before the layout animation is started on the UI thread. On iOS
this logic is always executed on the UI thread - that's why this issue
only occurred on Android. To combat this I moved all updates to
`layoutAninmations_` to the UI thread.
<table>
<tr><td width="50%">before</td><td width="50%">after</td></tr>
<tr><td>


https://github.com/user-attachments/assets/06c5c0fd-df0e-432a-9c54-2edd5fd6b7c4

</td><td>


https://github.com/user-attachments/assets/dc7de243-597b-48a8-bc65-a3e9d3c352c3

</td></tr>
</table>

## Test plan
Check in BB example if there is a flicker when triggering a layout
animation during an entering animation. Also stress test 2048 example.
  • Loading branch information
bartlomiejbloniarz authored Sep 19, 2024
1 parent ec712d6 commit b857008
Showing 1 changed file with 60 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ void LayoutAnimationsProxy::handleUpdatesAndEnterings(
ShadowViewMutationList &mutations,
const PropsParserContext &propsParserContext,
SurfaceId surfaceId) const {
std::unordered_map<Tag, ShadowView> oldShadowViewsForReparentings;
for (auto &mutation : mutations) {
maybeUpdateWindowDimensions(mutation, surfaceId);

Expand All @@ -292,7 +293,14 @@ void LayoutAnimationsProxy::handleUpdatesAndEnterings(
if (movedViews.contains(tag)) {
auto layoutAnimationIt = layoutAnimations_.find(tag);
if (layoutAnimationIt == layoutAnimations_.end()) {
filteredMutations.push_back(mutation);
if (oldShadowViewsForReparentings.contains(tag)) {
filteredMutations.push_back(ShadowViewMutation::InsertMutation(
mutation.parentShadowView,
oldShadowViewsForReparentings[tag],
mutation.index));
} else {
filteredMutations.push_back(mutation);
}
continue;
}

Expand Down Expand Up @@ -339,6 +347,10 @@ void LayoutAnimationsProxy::handleUpdatesAndEnterings(
updateOngoingAnimationTarget(tag, mutation);
continue;
}

// store the oldChildShadowView, so that we can use this ShadowView when
// the view is inserted
oldShadowViewsForReparentings[tag] = mutation.oldChildShadowView;
startLayoutAnimation(tag, mutation);
break;
}
Expand Down Expand Up @@ -603,30 +615,37 @@ void LayoutAnimationsProxy::startEnteringAnimation(
LOG(INFO) << "start entering animation for tag " << tag << std::endl;
#endif
auto finalView = std::make_shared<ShadowView>(mutation.newChildShadowView);
auto current = std::make_shared<ShadowView>(mutation.oldChildShadowView);
auto current = std::make_shared<ShadowView>(mutation.newChildShadowView);
auto parent = std::make_shared<ShadowView>(mutation.parentShadowView);

auto &viewProps =
static_cast<const ViewProps &>(*mutation.newChildShadowView.props);
layoutAnimations_.insert_or_assign(
tag, LayoutAnimation{finalView, current, parent, viewProps.opacity});
auto opacity = viewProps.opacity;

uiScheduler_->scheduleOnUI(
[finalView, current, parent, mutation, opacity, this, tag]() {
Rect window{};
{
auto lock = std::unique_lock<std::recursive_mutex>(mutex);
layoutAnimations_.insert_or_assign(
tag, LayoutAnimation{finalView, current, parent, opacity});
window =
surfaceManager.getWindow(mutation.newChildShadowView.surfaceId);
}

Snapshot values(
mutation.newChildShadowView,
surfaceManager.getWindow(mutation.newChildShadowView.surfaceId));
uiScheduler_->scheduleOnUI([values, this, tag]() {
jsi::Object yogaValues(uiRuntime_);
yogaValues.setProperty(uiRuntime_, "targetOriginX", values.x);
yogaValues.setProperty(uiRuntime_, "targetGlobalOriginX", values.x);
yogaValues.setProperty(uiRuntime_, "targetOriginY", values.y);
yogaValues.setProperty(uiRuntime_, "targetGlobalOriginY", values.y);
yogaValues.setProperty(uiRuntime_, "targetWidth", values.width);
yogaValues.setProperty(uiRuntime_, "targetHeight", values.height);
yogaValues.setProperty(uiRuntime_, "windowWidth", values.windowWidth);
yogaValues.setProperty(uiRuntime_, "windowHeight", values.windowHeight);
layoutAnimationsManager_->startLayoutAnimation(
uiRuntime_, tag, LayoutAnimationType::ENTERING, yogaValues);
});
Snapshot values(mutation.newChildShadowView, window);
jsi::Object yogaValues(uiRuntime_);
yogaValues.setProperty(uiRuntime_, "targetOriginX", values.x);
yogaValues.setProperty(uiRuntime_, "targetGlobalOriginX", values.x);
yogaValues.setProperty(uiRuntime_, "targetOriginY", values.y);
yogaValues.setProperty(uiRuntime_, "targetGlobalOriginY", values.y);
yogaValues.setProperty(uiRuntime_, "targetWidth", values.width);
yogaValues.setProperty(uiRuntime_, "targetHeight", values.height);
yogaValues.setProperty(uiRuntime_, "windowWidth", values.windowWidth);
yogaValues.setProperty(uiRuntime_, "windowHeight", values.windowHeight);
layoutAnimationsManager_->startLayoutAnimation(
uiRuntime_, tag, LayoutAnimationType::ENTERING, yogaValues);
});
}

void LayoutAnimationsProxy::startExitingAnimation(
Expand All @@ -636,12 +655,18 @@ void LayoutAnimationsProxy::startExitingAnimation(
LOG(INFO) << "start exiting animation for tag " << tag << std::endl;
#endif
auto surfaceId = mutation.oldChildShadowView.surfaceId;
auto oldView = mutation.oldChildShadowView;
createLayoutAnimation(mutation, oldView, surfaceId, tag);

Snapshot values(oldView, surfaceManager.getWindow(surfaceId));
uiScheduler_->scheduleOnUI([this, tag, mutation, surfaceId]() {
auto oldView = mutation.oldChildShadowView;
Rect window{};
{
auto lock = std::unique_lock<std::recursive_mutex>(mutex);
createLayoutAnimation(mutation, oldView, surfaceId, tag);
window = surfaceManager.getWindow(surfaceId);
}

Snapshot values(oldView, window);

uiScheduler_->scheduleOnUI([values, this, tag]() {
jsi::Object yogaValues(uiRuntime_);
yogaValues.setProperty(uiRuntime_, "currentOriginX", values.x);
yogaValues.setProperty(uiRuntime_, "currentGlobalOriginX", values.x);
Expand All @@ -664,14 +689,19 @@ void LayoutAnimationsProxy::startLayoutAnimation(
LOG(INFO) << "start layout animation for tag " << tag << std::endl;
#endif
auto surfaceId = mutation.oldChildShadowView.surfaceId;
auto oldView = mutation.oldChildShadowView;
createLayoutAnimation(mutation, oldView, surfaceId, tag);

Snapshot currentValues(oldView, surfaceManager.getWindow(surfaceId));
Snapshot targetValues(
mutation.newChildShadowView, surfaceManager.getWindow(surfaceId));
uiScheduler_->scheduleOnUI([this, mutation, surfaceId, tag]() {
auto oldView = mutation.oldChildShadowView;
Rect window{};
{
auto lock = std::unique_lock<std::recursive_mutex>(mutex);
createLayoutAnimation(mutation, oldView, surfaceId, tag);
window = surfaceManager.getWindow(surfaceId);
}

Snapshot currentValues(oldView, window);
Snapshot targetValues(mutation.newChildShadowView, window);

uiScheduler_->scheduleOnUI([currentValues, targetValues, this, tag]() {
jsi::Object yogaValues(uiRuntime_);
yogaValues.setProperty(uiRuntime_, "currentOriginX", currentValues.x);
yogaValues.setProperty(uiRuntime_, "currentGlobalOriginX", currentValues.x);
Expand Down

0 comments on commit b857008

Please sign in to comment.