#include #include #include #include #include #include namespace reanimated { // We never modify the Shadow Tree, we just send some additional // mutations to the mounting layer. // When animations finish, the Host Tree will represent the most recent Shadow // Tree // On android this code will be sometimes executed on the JS thread. // That's why we have to schedule some of animation manager function on the UI // thread std::optional LayoutAnimationsProxy::pullTransaction( SurfaceId surfaceId, MountingTransaction::Number transactionNumber, const TransactionTelemetry &telemetry, ShadowViewMutationList mutations) const { #ifdef LAYOUT_ANIMATIONS_LOGS LOG(INFO) << std::endl; LOG(INFO) << "pullTransaction " << std::this_thread::get_id() << " " << surfaceId << std::endl; #endif auto lock = std::unique_lock(mutex); PropsParserContext propsParserContext{surfaceId, *contextContainer_}; ShadowViewMutationList filteredMutations; std::vector> roots; std::unordered_map movedViews; addOngoingAnimations(surfaceId, filteredMutations); for (const auto tag : finishedAnimationTags_) { auto &updateMap = surfaceManager.getUpdateMap(surfaceId); layoutAnimations_.erase(tag); updateMap.erase(tag); } finishedAnimationTags_.clear(); parseRemoveMutations(movedViews, mutations, roots); handleRemovals(filteredMutations, roots); handleUpdatesAndEnterings( filteredMutations, movedViews, mutations, propsParserContext, surfaceId); addOngoingAnimations(surfaceId, filteredMutations); return MountingTransaction{ surfaceId, transactionNumber, std::move(filteredMutations), telemetry}; } std::optional LayoutAnimationsProxy::progressLayoutAnimation( int tag, const jsi::Object &newStyle) { #ifdef LAYOUT_ANIMATIONS_LOGS LOG(INFO) << "progress layout animation for tag " << tag << std::endl; #endif auto lock = std::unique_lock(mutex); auto layoutAnimationIt = layoutAnimations_.find(tag); if (layoutAnimationIt == layoutAnimations_.end()) { return {}; } auto &layoutAnimation = layoutAnimationIt->second; maybeRestoreOpacity(layoutAnimation, newStyle); auto rawProps = std::make_shared(uiRuntime_, jsi::Value(uiRuntime_, newStyle)); PropsParserContext propsParserContext{ layoutAnimation.finalView->surfaceId, *contextContainer_}; #ifdef RN_SERIALIZABLE_STATE rawProps = std::make_shared(folly::dynamic::merge( layoutAnimation.finalView->props->rawProps, (folly::dynamic)*rawProps)); #endif auto newProps = getComponentDescriptorForShadowView(*layoutAnimation.finalView) .cloneProps( propsParserContext, layoutAnimation.finalView->props, std::move(*rawProps)); auto &updateMap = surfaceManager.getUpdateMap(layoutAnimation.finalView->surfaceId); updateMap.insert_or_assign( tag, UpdateValues{newProps, Frame(uiRuntime_, newStyle)}); return layoutAnimation.finalView->surfaceId; } std::optional LayoutAnimationsProxy::endLayoutAnimation( int tag, bool shouldRemove) { #ifdef LAYOUT_ANIMATIONS_LOGS LOG(INFO) << "end layout animation for " << tag << " - should remove " << shouldRemove << std::endl; #endif auto lock = std::unique_lock(mutex); auto layoutAnimationIt = layoutAnimations_.find(tag); if (layoutAnimationIt == layoutAnimations_.end()) { return {}; } auto &layoutAnimation = layoutAnimationIt->second; // multiple layout animations can be triggered for a view // one after the other, so we need to keep count of how many // were actually triggered, so that we don't cleanup necessary // structures too early if (layoutAnimation.count > 1) { layoutAnimation.count--; return {}; } finishedAnimationTags_.push_back(tag); auto surfaceId = layoutAnimation.finalView->surfaceId; if (!shouldRemove || !nodeForTag_.contains(tag)) { return {}; } auto node = nodeForTag_[tag]; auto mutationNode = std::static_pointer_cast(node); mutationNode->state = DEAD; deadNodes.insert(mutationNode); return surfaceId; } /** Organizes removed views into a tree structure, allowing for convenient traversals and index maintenance */ void LayoutAnimationsProxy::parseRemoveMutations( std::unordered_map &movedViews, ShadowViewMutationList &mutations, std::vector> &roots) const { std::set deletedViews; std::unordered_map>> childrenForTag, unflattenedChildrenForTag; std::vector> mutationNodes; // iterate from the end, so that parents appear before children for (auto it = mutations.rbegin(); it != mutations.rend(); it++) { auto &mutation = *it; if (mutation.type == ShadowViewMutation::Delete) { deletedViews.insert(mutation.oldChildShadowView.tag); } if (mutation.type == ShadowViewMutation::Remove) { updateIndexForMutation(mutation); auto tag = mutation.oldChildShadowView.tag; auto parentTag = mutation.parentTag; auto unflattenedParentTag = parentTag; // temporary std::shared_ptr mutationNode; std::shared_ptr node = nodeForTag_[tag], parent = nodeForTag_[parentTag], unflattenedParent = nodeForTag_[unflattenedParentTag]; if (!node) { mutationNode = std::make_shared(mutation); } else { mutationNode = std::make_shared(mutation, std::move(*node)); for (auto &subNode : mutationNode->children) { subNode->parent = mutationNode; } for (auto &subNode : mutationNode->unflattenedChildren) { subNode->unflattenedParent = mutationNode; } } if (!deletedViews.contains(mutation.oldChildShadowView.tag)) { mutationNode->state = MOVED; movedViews.insert_or_assign(mutation.oldChildShadowView.tag, -1); } nodeForTag_[tag] = mutationNode; if (!parent) { parent = std::make_shared(parentTag); nodeForTag_[parentTag] = parent; } if (!unflattenedParent) { if (parentTag == unflattenedParentTag) { unflattenedParent = parent; } else { unflattenedParent = std::make_shared(unflattenedParentTag); nodeForTag_[unflattenedParentTag] = unflattenedParent; } } mutationNodes.push_back(mutationNode); childrenForTag[parentTag].push_back(mutationNode); unflattenedChildrenForTag[unflattenedParentTag].push_back(mutationNode); mutationNode->parent = parent; mutationNode->unflattenedParent = unflattenedParent; } if (mutation.type == ShadowViewMutation::Update && movedViews.contains(mutation.newChildShadowView.tag)) { auto node = nodeForTag_[mutation.newChildShadowView.tag]; auto mutationNode = std::static_pointer_cast(node); mutationNode->mutation.oldChildShadowView = mutation.oldChildShadowView; } } for (const auto &mutation : mutations) { if (mutation.type == ShadowViewMutation::Insert && movedViews.contains(mutation.newChildShadowView.tag)) { movedViews[mutation.newChildShadowView.tag] = mutation.parentTag; } } for (auto &[parentTag, children] : childrenForTag) { auto &parent = nodeForTag_[parentTag]; parent->insertChildren(children); for (auto &child : children) { child->parent = parent; } } for (auto &[unflattenedParentTag, children] : unflattenedChildrenForTag) { auto &unflattenedParent = nodeForTag_[unflattenedParentTag]; unflattenedParent->insertUnflattenedChildren(children); for (auto &child : children) { child->unflattenedParent = unflattenedParent; } } for (auto &mutationNode : mutationNodes) { if (!mutationNode->unflattenedParent->isMutationMode()) { roots.push_back(mutationNode); } } } void LayoutAnimationsProxy::handleRemovals( ShadowViewMutationList &filteredMutations, std::vector> &roots) const { // iterate from the end, so that children // with higher indices appear first in the mutations list for (auto it = roots.rbegin(); it != roots.rend(); it++) { auto &node = *it; if (!startAnimationsRecursively( node, true, true, false, filteredMutations)) { filteredMutations.push_back(node->mutation); node->unflattenedParent->removeChildFromUnflattenedTree(node); //??? if (node->state != MOVED) { maybeCancelAnimation(node->tag); filteredMutations.push_back(ShadowViewMutation::DeleteMutation( node->mutation.oldChildShadowView)); nodeForTag_.erase(node->tag); node->state = DELETED; #ifdef LAYOUT_ANIMATIONS_LOGS LOG(INFO) << "delete " << node->tag << std::endl; #endif } } } for (auto node : deadNodes) { if (node->state != DELETED) { endAnimationsRecursively(node, filteredMutations); maybeDropAncestors(node->unflattenedParent, node, filteredMutations); } } deadNodes.clear(); } void LayoutAnimationsProxy::handleUpdatesAndEnterings( ShadowViewMutationList &filteredMutations, const std::unordered_map &movedViews, ShadowViewMutationList &mutations, const PropsParserContext &propsParserContext, SurfaceId surfaceId) const { std::unordered_map oldShadowViewsForReparentings; for (auto &mutation : mutations) { maybeUpdateWindowDimensions(mutation, surfaceId); Tag tag = mutation.type == ShadowViewMutation::Type::Create || mutation.type == ShadowViewMutation::Type::Insert ? mutation.newChildShadowView.tag : mutation.oldChildShadowView.tag; switch (mutation.type) { case ShadowViewMutation::Type::Create: { filteredMutations.push_back(mutation); break; } case ShadowViewMutation::Type::Insert: { updateIndexForMutation(mutation); const auto parentTag = mutation.parentTag; const auto mutationParent = parentTag; if (nodeForTag_.contains(parentTag)) { nodeForTag_[parentTag]->applyMutationToIndices(mutation); } if (movedViews.contains(tag)) { auto layoutAnimationIt = layoutAnimations_.find(tag); if (layoutAnimationIt == layoutAnimations_.end()) { if (oldShadowViewsForReparentings.contains(tag)) { filteredMutations.push_back(ShadowViewMutation::InsertMutation( mutationParent, oldShadowViewsForReparentings[tag], mutation.index)); } else { filteredMutations.push_back(mutation); } continue; } auto oldView = *layoutAnimationIt->second.currentView; filteredMutations.push_back(ShadowViewMutation::InsertMutation( mutationParent, oldView, mutation.index)); if (movedViews.contains(tag)) { layoutAnimationIt->second.parentTag = movedViews.at(tag); } continue; } transferConfigFromNativeID( mutation.newChildShadowView.props->nativeId, mutation.newChildShadowView.tag); if (!layoutAnimationsManager_->hasLayoutAnimation(tag, ENTERING)) { filteredMutations.push_back(mutation); continue; } startEnteringAnimation(tag, mutation); filteredMutations.push_back(mutation); // temporarily set opacity to 0 to prevent flickering on android std::shared_ptr newView = cloneViewWithoutOpacity(mutation, propsParserContext); filteredMutations.push_back(ShadowViewMutation::UpdateMutation( mutation.newChildShadowView, *newView, mutationParent)); break; } case ShadowViewMutation::Type::Update: { auto shouldAnimate = hasLayoutChanged(mutation); if (!layoutAnimationsManager_->hasLayoutAnimation(tag, LAYOUT) || (!shouldAnimate && !layoutAnimations_.contains(tag))) { // We should cancel any ongoing animation here to ensure that the // proper final state is reached for this view However, due to how // RNSScreens handle adding headers (a second commit is triggered to // offset all the elements by the header height) this would lead to // all entering animations being cancelled when a screen with a header // is pushed onto a stack // TODO: find a better solution for this problem filteredMutations.push_back(mutation); continue; } else if (!shouldAnimate) { updateOngoingAnimationTarget(tag, mutation); continue; } // store the oldChildShadowView, so that we can use this ShadowView when // the view is inserted oldShadowViewsForReparentings[tag] = mutation.oldChildShadowView; if (movedViews.contains(tag)) { mutation.parentTag = movedViews.at(tag); } if (mutation.parentTag != -1) { startLayoutAnimation(tag, mutation); } break; } case ShadowViewMutation::Type::Remove: case ShadowViewMutation::Type::Delete: { break; } default: filteredMutations.push_back(mutation); } } } void LayoutAnimationsProxy::addOngoingAnimations( SurfaceId surfaceId, ShadowViewMutationList &mutations) const { auto &updateMap = surfaceManager.getUpdateMap(surfaceId); for (auto &[tag, updateValues] : updateMap) { auto layoutAnimationIt = layoutAnimations_.find(tag); if (layoutAnimationIt == layoutAnimations_.end()) { continue; } auto &layoutAnimation = layoutAnimationIt->second; auto newView = std::make_shared(*layoutAnimation.finalView); newView->props = updateValues.newProps; updateLayoutMetrics(newView->layoutMetrics, updateValues.frame); mutations.push_back(ShadowViewMutation::UpdateMutation( *layoutAnimation.currentView, *newView, layoutAnimation.parentTag)); layoutAnimation.currentView = newView; } updateMap.clear(); } void LayoutAnimationsProxy::endAnimationsRecursively( std::shared_ptr node, ShadowViewMutationList &mutations) const { maybeCancelAnimation(node->tag); node->state = DELETED; // iterate from the end, so that children // with higher indices appear first in the mutations list for (auto it = node->unflattenedChildren.rbegin(); it != node->unflattenedChildren.rend(); it++) { auto &subNode = *it; if (subNode->state != DELETED) { endAnimationsRecursively(subNode, mutations); } } mutations.push_back(node->mutation); nodeForTag_.erase(node->tag); #ifdef LAYOUT_ANIMATIONS_LOGS LOG(INFO) << "delete " << node->tag << std::endl; #endif mutations.push_back( ShadowViewMutation::DeleteMutation(node->mutation.oldChildShadowView)); } void LayoutAnimationsProxy::maybeDropAncestors( std::shared_ptr parent, std::shared_ptr child, ShadowViewMutationList &cleanupMutations) const { parent->removeChildFromUnflattenedTree(child); if (!parent->isMutationMode()) { return; } auto node = std::static_pointer_cast(parent); if (node->children.size() == 0 && node->state != ANIMATING) { nodeForTag_.erase(node->tag); cleanupMutations.push_back(node->mutation); maybeCancelAnimation(node->tag); node->state = DELETED; #ifdef LAYOUT_ANIMATIONS_LOGS LOG(INFO) << "delete " << node->tag << std::endl; #endif cleanupMutations.push_back( ShadowViewMutation::DeleteMutation(node->mutation.oldChildShadowView)); maybeDropAncestors(node->unflattenedParent, node, cleanupMutations); } } const ComponentDescriptor & LayoutAnimationsProxy::getComponentDescriptorForShadowView( const ShadowView &shadowView) const { return componentDescriptorRegistry_->at(shadowView.componentHandle); } bool LayoutAnimationsProxy::startAnimationsRecursively( std::shared_ptr node, bool shouldRemoveSubviewsWithoutAnimations, bool shouldAnimate, bool isScreenPop, ShadowViewMutationList &mutations) const { if (isRNSScreen(node)) { isScreenPop = true; } shouldAnimate = !isScreenPop && layoutAnimationsManager_->shouldAnimateExiting(node->tag, shouldAnimate); bool hasExitAnimation = shouldAnimate && layoutAnimationsManager_->hasLayoutAnimation( node->tag, LayoutAnimationType::EXITING); bool hasAnimatedChildren = false; shouldRemoveSubviewsWithoutAnimations = shouldRemoveSubviewsWithoutAnimations && (!hasExitAnimation || node->state == MOVED); std::vector> toBeRemoved; // iterate from the end, so that children // with higher indices appear first in the mutations list for (auto it = node->unflattenedChildren.rbegin(); it != node->unflattenedChildren.rend(); it++) { auto &subNode = *it; #ifdef LAYOUT_ANIMATIONS_LOGS LOG(INFO) << "child " << subNode->tag << " " << " " << shouldAnimate << " " << shouldRemoveSubviewsWithoutAnimations << std::endl; #endif if (subNode->state != UNDEFINED && subNode->state != MOVED) { if (shouldAnimate && subNode->state != DEAD) { hasAnimatedChildren = true; } else { endAnimationsRecursively(subNode, mutations); toBeRemoved.push_back(subNode); } } else if (startAnimationsRecursively( subNode, shouldRemoveSubviewsWithoutAnimations, shouldAnimate, isScreenPop, mutations)) { #ifdef LAYOUT_ANIMATIONS_LOGS LOG(INFO) << "child " << subNode->tag << " start animations returned true " << std::endl; #endif hasAnimatedChildren = true; } else if (subNode->state == MOVED) { mutations.push_back(subNode->mutation); toBeRemoved.push_back(subNode); } else if (shouldRemoveSubviewsWithoutAnimations) { maybeCancelAnimation(subNode->tag); mutations.push_back(subNode->mutation); toBeRemoved.push_back(subNode); subNode->state = DELETED; nodeForTag_.erase(subNode->tag); #ifdef LAYOUT_ANIMATIONS_LOGS LOG(INFO) << "delete " << subNode->tag << std::endl; #endif mutations.push_back(ShadowViewMutation::DeleteMutation( subNode->mutation.oldChildShadowView)); } else { subNode->state = WAITING; } } for (auto &subNode : toBeRemoved) { node->removeChildFromUnflattenedTree(subNode); } if (node->state == MOVED) { auto replacement = std::make_shared(*node); for (auto subNode : node->children) { subNode->parent = replacement; } for (auto subNode : node->unflattenedChildren) { subNode->unflattenedParent = replacement; } nodeForTag_[replacement->tag] = replacement; return false; } bool wantAnimateExit = hasExitAnimation || hasAnimatedChildren; if (hasExitAnimation) { node->state = ANIMATING; startExitingAnimation(node->tag, node->mutation); } else { layoutAnimationsManager_->clearLayoutAnimationConfig(node->tag); } return wantAnimateExit; } void LayoutAnimationsProxy::updateIndexForMutation( ShadowViewMutation &mutation) const { if (mutation.index == -1) { return; } const auto parentTag = mutation.parentTag; if (!nodeForTag_.contains(parentTag)) { return; } auto parent = nodeForTag_[parentTag]; int size = 0, prevIndex = -1, offset = 0; for (auto &subNode : parent->children) { size += subNode->mutation.index - prevIndex - 1; if (mutation.index < size) { break; } offset++; prevIndex = subNode->mutation.index; } #ifdef LAYOUT_ANIMATIONS_LOGS int tag = mutation.type == ShadowViewMutation::Insert ? mutation.newChildShadowView.tag : mutation.oldChildShadowView.tag; LOG(INFO) << "update index for " << tag << " in " << parentTag << ": " << mutation.index << " -> " << mutation.index + offset << std::endl; #endif mutation.index += offset; } bool LayoutAnimationsProxy::shouldOverridePullTransaction() const { return true; } void LayoutAnimationsProxy::createLayoutAnimation( const ShadowViewMutation &mutation, ShadowView &oldView, const SurfaceId &surfaceId, const int tag) const { int count = 1; auto layoutAnimationIt = layoutAnimations_.find(tag); if (layoutAnimationIt != layoutAnimations_.end()) { auto &layoutAnimation = layoutAnimationIt->second; oldView = *layoutAnimation.currentView; count = layoutAnimation.count + 1; } auto finalView = std::make_shared( mutation.type == ShadowViewMutation::Remove ? mutation.oldChildShadowView : mutation.newChildShadowView); auto currentView = std::make_shared(oldView); layoutAnimations_.insert_or_assign( tag, LayoutAnimation{finalView, currentView, mutation.parentTag, {}, count}); } void LayoutAnimationsProxy::startEnteringAnimation( const int tag, ShadowViewMutation &mutation) const { #ifdef LAYOUT_ANIMATIONS_LOGS LOG(INFO) << "start entering animation for tag " << tag << std::endl; #endif auto finalView = std::make_shared(mutation.newChildShadowView); auto current = std::make_shared(mutation.newChildShadowView); auto &viewProps = static_cast(*mutation.newChildShadowView.props); auto opacity = viewProps.opacity; uiScheduler_->scheduleOnUI([weakThis = weak_from_this(), finalView, current, mutation, opacity, tag]() { auto strongThis = weakThis.lock(); if (!strongThis) { return; } Rect window{}; { auto &mutex = strongThis->mutex; auto lock = std::unique_lock(mutex); strongThis->layoutAnimations_.insert_or_assign( tag, LayoutAnimation{finalView, current, mutation.parentTag, opacity}); window = strongThis->surfaceManager.getWindow( mutation.newChildShadowView.surfaceId); } Snapshot values(mutation.newChildShadowView, window); auto &uiRuntime = strongThis->uiRuntime_; 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); strongThis->layoutAnimationsManager_->startLayoutAnimation( uiRuntime, tag, LayoutAnimationType::ENTERING, yogaValues); }); } void LayoutAnimationsProxy::startExitingAnimation( const int tag, ShadowViewMutation &mutation) const { #ifdef LAYOUT_ANIMATIONS_LOGS LOG(INFO) << "start exiting animation for tag " << tag << std::endl; #endif auto surfaceId = mutation.oldChildShadowView.surfaceId; uiScheduler_->scheduleOnUI( [weakThis = weak_from_this(), tag, mutation, surfaceId]() { auto strongThis = weakThis.lock(); if (!strongThis) { return; } auto oldView = mutation.oldChildShadowView; Rect window{}; { auto &mutex = strongThis->mutex; auto lock = std::unique_lock(mutex); strongThis->createLayoutAnimation(mutation, oldView, surfaceId, tag); window = strongThis->surfaceManager.getWindow(surfaceId); } Snapshot values(oldView, window); auto &uiRuntime = strongThis->uiRuntime_; jsi::Object yogaValues(uiRuntime); yogaValues.setProperty(uiRuntime, "currentOriginX", values.x); yogaValues.setProperty(uiRuntime, "currentGlobalOriginX", values.x); yogaValues.setProperty(uiRuntime, "currentOriginY", values.y); yogaValues.setProperty(uiRuntime, "currentGlobalOriginY", values.y); yogaValues.setProperty(uiRuntime, "currentWidth", values.width); yogaValues.setProperty(uiRuntime, "currentHeight", values.height); yogaValues.setProperty(uiRuntime, "windowWidth", values.windowWidth); yogaValues.setProperty(uiRuntime, "windowHeight", values.windowHeight); strongThis->layoutAnimationsManager_->startLayoutAnimation( uiRuntime, tag, LayoutAnimationType::EXITING, yogaValues); strongThis->layoutAnimationsManager_->clearLayoutAnimationConfig(tag); }); } void LayoutAnimationsProxy::startLayoutAnimation( const int tag, const ShadowViewMutation &mutation) const { #ifdef LAYOUT_ANIMATIONS_LOGS LOG(INFO) << "start layout animation for tag " << tag << std::endl; #endif auto surfaceId = mutation.oldChildShadowView.surfaceId; uiScheduler_->scheduleOnUI([weakThis = weak_from_this(), mutation, surfaceId, tag]() { auto strongThis = weakThis.lock(); if (!strongThis) { return; } auto oldView = mutation.oldChildShadowView; Rect window{}; { auto &mutex = strongThis->mutex; auto lock = std::unique_lock(mutex); strongThis->createLayoutAnimation(mutation, oldView, surfaceId, tag); window = strongThis->surfaceManager.getWindow(surfaceId); } Snapshot currentValues(oldView, window); Snapshot targetValues(mutation.newChildShadowView, window); auto &uiRuntime = strongThis->uiRuntime_; jsi::Object yogaValues(uiRuntime); yogaValues.setProperty(uiRuntime, "currentOriginX", currentValues.x); yogaValues.setProperty(uiRuntime, "currentGlobalOriginX", currentValues.x); yogaValues.setProperty(uiRuntime, "currentOriginY", currentValues.y); yogaValues.setProperty(uiRuntime, "currentGlobalOriginY", currentValues.y); yogaValues.setProperty(uiRuntime, "currentWidth", currentValues.width); yogaValues.setProperty(uiRuntime, "currentHeight", currentValues.height); yogaValues.setProperty(uiRuntime, "targetOriginX", targetValues.x); yogaValues.setProperty(uiRuntime, "targetGlobalOriginX", targetValues.x); yogaValues.setProperty(uiRuntime, "targetOriginY", targetValues.y); yogaValues.setProperty(uiRuntime, "targetGlobalOriginY", targetValues.y); yogaValues.setProperty(uiRuntime, "targetWidth", targetValues.width); yogaValues.setProperty(uiRuntime, "targetHeight", targetValues.height); yogaValues.setProperty(uiRuntime, "windowWidth", targetValues.windowWidth); yogaValues.setProperty( uiRuntime, "windowHeight", targetValues.windowHeight); strongThis->layoutAnimationsManager_->startLayoutAnimation( uiRuntime, tag, LayoutAnimationType::LAYOUT, yogaValues); }); } void LayoutAnimationsProxy::updateOngoingAnimationTarget( const int tag, const ShadowViewMutation &mutation) const { layoutAnimations_[tag].finalView = std::make_shared(mutation.newChildShadowView); } void LayoutAnimationsProxy::maybeCancelAnimation(const int tag) const { if (!layoutAnimations_.contains(tag)) { return; } layoutAnimations_.erase(tag); uiScheduler_->scheduleOnUI([weakThis = weak_from_this(), tag]() { auto strongThis = weakThis.lock(); if (!strongThis) { return; } auto &uiRuntime = strongThis->uiRuntime_; strongThis->layoutAnimationsManager_->cancelLayoutAnimation(uiRuntime, tag); }); } void LayoutAnimationsProxy::transferConfigFromNativeID( const std::string nativeIdString, const int tag) const { if (nativeIdString.empty()) { return; } try { auto nativeId = stoi(nativeIdString); layoutAnimationsManager_->transferConfigFromNativeID(nativeId, tag); } catch (std::invalid_argument) { } catch (std::out_of_range) { } } // When entering animations start, we temporarily set opacity to 0 // so that we can immediately insert the view at the right position // and schedule the animation on the UI thread std::shared_ptr LayoutAnimationsProxy::cloneViewWithoutOpacity( facebook::react::ShadowViewMutation &mutation, const PropsParserContext &propsParserContext) const { auto newView = std::make_shared(mutation.newChildShadowView); folly::dynamic opacity = folly::dynamic::object("opacity", 0); auto newProps = getComponentDescriptorForShadowView(*newView).cloneProps( propsParserContext, newView->props, RawProps(opacity)); newView->props = newProps; return newView; } void LayoutAnimationsProxy::maybeRestoreOpacity( LayoutAnimation &layoutAnimation, const jsi::Object &newStyle) const { if (layoutAnimation.opacity && !newStyle.hasProperty(uiRuntime_, "opacity")) { newStyle.setProperty( uiRuntime_, "opacity", jsi::Value(*layoutAnimation.opacity)); layoutAnimation.opacity.reset(); } } void LayoutAnimationsProxy::maybeUpdateWindowDimensions( facebook::react::ShadowViewMutation &mutation, SurfaceId surfaceId) const { if (mutation.type == ShadowViewMutation::Update && !std::strcmp( mutation.oldChildShadowView.componentName, RootComponentName)) { surfaceManager.updateWindow( surfaceId, mutation.newChildShadowView.layoutMetrics.frame.size.width, mutation.newChildShadowView.layoutMetrics.frame.size.height); } } } // namespace reanimated