From 40329454ea85ea1a64280fe2bac8a5c56f7ddb4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Wed, 16 Apr 2025 12:14:24 +0200 Subject: [PATCH 01/90] Animate a hard-coded clone --- apps/common-app/src/App.tsx | 2 +- .../apps/reanimated/examples/EmptyExample.tsx | 131 ++++++++++++++++-- apps/fabric-example/ios/Podfile.lock | 2 +- .../LayoutAnimations/LayoutAnimationType.h | 1 + .../LayoutAnimationsManager.cpp | 2 + .../LayoutAnimationsManager.h | 3 +- .../LayoutAnimationsProxy.cpp | 64 +++++++++ .../LayoutAnimations/LayoutAnimationsProxy.h | 7 +- .../src/commonTypes.ts | 1 + .../AnimatedComponent.tsx | 13 ++ .../createAnimatedComponent/commonTypes.ts | 1 + .../layoutReanimation/animationsManager.ts | 2 + 12 files changed, 217 insertions(+), 12 deletions(-) diff --git a/apps/common-app/src/App.tsx b/apps/common-app/src/App.tsx index ce1ac42899e3..46cb65114e08 100644 --- a/apps/common-app/src/App.tsx +++ b/apps/common-app/src/App.tsx @@ -45,7 +45,7 @@ export default function App() { getPathFromState(state, options).replace(/%2F/g, '/'), diff --git a/apps/common-app/src/apps/reanimated/examples/EmptyExample.tsx b/apps/common-app/src/apps/reanimated/examples/EmptyExample.tsx index 7f3a1ac26ce1..b6f705565f12 100644 --- a/apps/common-app/src/apps/reanimated/examples/EmptyExample.tsx +++ b/apps/common-app/src/apps/reanimated/examples/EmptyExample.tsx @@ -1,18 +1,133 @@ -import React from 'react'; -import { StyleSheet, Text, View } from 'react-native'; +import type { + NativeStackNavigationProp, + NativeStackScreenProps, +} from '@react-navigation/native-stack'; +import { createNativeStackNavigator } from '@react-navigation/native-stack'; +import * as React from 'react'; +import { StyleSheet, TouchableNativeFeedback, View } from 'react-native'; +import Animated from 'react-native-reanimated'; + +type ParamList = { + Screen1?: object; + Screen2: { + title: string; + sharedTransitionTag: string; + }; +}; + +const Stack = createNativeStackNavigator(); + +interface CardProps { + navigation: NativeStackNavigationProp; + title: string; + transitionTag: string; + isOpen?: boolean; + nextScreen: keyof ParamList; +} + +function Card({ + navigation, + title, + transitionTag, + isOpen = false, + nextScreen, +}: CardProps) { + const goNext = (screenName: keyof ParamList) => { + navigation.navigate(screenName, { + title, + sharedTransitionTag: transitionTag, + }); + }; -export default function EmptyExample() { return ( - - Hello world! + { + goNext(nextScreen); + }}> + + + ); +} + +function Screen1({ navigation }: NativeStackScreenProps) { + return ( + + {[...Array(1)].map((_, i) => ( + + ))} + + ); +} + +function Screen2({ + route, + navigation, +}: NativeStackScreenProps) { + const { title, sharedTransitionTag } = route.params; + + return ( + + ); } +export default function CardExample() { + return ( + + + + + ); +} const styles = StyleSheet.create({ - container: { + flexOne: { flex: 1, - alignItems: 'center', - justifyContent: 'center', + }, + open: { + height: 500, + marginTop: 50, + backgroundColor: 'green', + // opacity: 0.5, + }, + closed: { + height: 120, + marginTop: 20, + backgroundColor: 'green', + }, + text: { + width: '100%', + height: 20, + }, + fullWidth: { + width: '100%', }, }); diff --git a/apps/fabric-example/ios/Podfile.lock b/apps/fabric-example/ios/Podfile.lock index 18f768f65b74..006debbce3da 100644 --- a/apps/fabric-example/ios/Podfile.lock +++ b/apps/fabric-example/ios/Podfile.lock @@ -2489,7 +2489,7 @@ SPEC CHECKSUMS: RNSVG: 794f269526df9ddc1f79b3d1a202b619df0368e3 RNWorklets: 228763f863587fbcedfdabf24ce8fe864c7d94f1 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 - Yoga: 7fb3f48a328f20ea5d5eecd862e91798bd76b255 + Yoga: 9773f1327b258fa449988c2e42fbb7cbdf655d96 PODFILE CHECKSUM: e19c71a6204ef86abcaad82da473c338754ef94c diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationType.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationType.h index 6c36ae057a0a..8da069b27575 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationType.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationType.h @@ -4,4 +4,5 @@ typedef enum LayoutAnimationType { ENTERING = 1, EXITING = 2, LAYOUT = 3, + SHARED_ELEMENT_TRANSITION = 4, } LayoutAnimationType; diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.cpp index 5e5c55b01483..c253f0b34f94 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.cpp @@ -117,6 +117,8 @@ LayoutAnimationsManager::getConfigsForType(const LayoutAnimationType type) { return exitingAnimations_; case LAYOUT: return layoutAnimations_; + case SHARED_ELEMENT_TRANSITION: + return sharedTransitions_; default: assert(false); } diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.h index 42eff6f6e866..422d488a7c1e 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.h @@ -44,14 +44,15 @@ class LayoutAnimationsManager { void cancelLayoutAnimation(jsi::Runtime &rt, const int tag) const; void transferConfigFromNativeID(const int nativeId, const int tag); - private: std::unordered_map> &getConfigsForType( const LayoutAnimationType type); +private: std::shared_ptr jsLogger_; std::unordered_map> enteringAnimationsForNativeID_; + std::unordered_map> sharedTransitions_; std::unordered_map> enteringAnimations_; std::unordered_map> exitingAnimations_; std::unordered_map> layoutAnimations_; diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index 2d8dacf00a67..0171c3629310 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -300,6 +300,25 @@ void LayoutAnimationsProxy::handleUpdatesAndEnterings( if (nodeForTag_.contains(parentTag)) { nodeForTag_[parentTag]->applyMutationToIndices(mutation); } + + if (layoutAnimationsManager_->hasLayoutAnimation(tag, SHARED_ELEMENT_TRANSITION)){ + if (previousView){ + ShadowView s = *previousView; + s.tag = myTag; + s.layoutMetrics.frame.origin.y += 100; + filteredMutations.push_back(ShadowViewMutation::CreateMutation(s)); + filteredMutations.push_back(ShadowViewMutation::InsertMutation(1, s, 1)); + layoutAnimationsManager_->getConfigsForType(LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[myTag] = layoutAnimationsManager_->getConfigsForType(LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[previousView->tag]; + mutation.newChildShadowView.tag = myTag; + previousView->tag = myTag; + mutation.newChildShadowView.layoutMetrics.frame.origin.y += 100; + previousView->layoutMetrics.frame.origin.y += 100; + startSharedTransition(myTag, *previousView, mutation.newChildShadowView); + myTag+=2; + continue; + } + previousView = mutation.newChildShadowView; + } if (movedViews.contains(tag)) { auto layoutAnimationIt = layoutAnimations_.find(tag); @@ -790,6 +809,51 @@ void LayoutAnimationsProxy::startLayoutAnimation( }); } +void LayoutAnimationsProxy::startSharedTransition(const int tag, const ShadowView &before, const ShadowView &after) const{ + auto surfaceId = 1; + + uiScheduler_->scheduleOnUI([weakThis = weak_from_this(), + before, + after, + surfaceId, + tag]() { + auto strongThis = weakThis.lock(); + if (!strongThis) { + return; + } + + auto oldView = before; + Rect window{}; + { + auto &mutex = strongThis->mutex; + auto lock = std::unique_lock(mutex); + strongThis->createLayoutAnimation(ShadowViewMutation::InsertMutation(1, after, 1), oldView, surfaceId, tag); + window = strongThis->surfaceManager.getWindow(surfaceId); + } + + Snapshot currentValues(oldView, window); + Snapshot targetValues(after, 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::SHARED_ELEMENT_TRANSITION, yogaValues); + }); +} + void LayoutAnimationsProxy::updateOngoingAnimationTarget( const int tag, const ShadowViewMutation &mutation) const { diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h index bf58a47f98ac..fda5e4a915fb 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h @@ -42,6 +42,9 @@ struct LayoutAnimationsProxy mutable SurfaceManager surfaceManager; mutable std::unordered_set> deadNodes; mutable std::unordered_map leastRemoved; + mutable int myTag = 10001; +// mutable std::unordered_map< + mutable std::optional previousView; std::shared_ptr layoutAnimationsManager_; ContextContainer::Shared contextContainer_; SharedComponentDescriptorRegistry componentDescriptorRegistry_; @@ -64,7 +67,9 @@ struct LayoutAnimationsProxy void startExitingAnimation(const int tag, ShadowViewMutation &mutation) const; void startLayoutAnimation(const int tag, const ShadowViewMutation &mutation) const; - + void startSharedTransition(const int tag, const ShadowView &before, const ShadowView &after) + const; + void transferConfigFromNativeID(const std::string nativeId, const int tag) const; std::optional progressLayoutAnimation( diff --git a/packages/react-native-reanimated/src/commonTypes.ts b/packages/react-native-reanimated/src/commonTypes.ts index 1798698487da..b93707653b23 100644 --- a/packages/react-native-reanimated/src/commonTypes.ts +++ b/packages/react-native-reanimated/src/commonTypes.ts @@ -89,6 +89,7 @@ export enum LayoutAnimationType { ENTERING = 1, EXITING = 2, LAYOUT = 3, + SHARED_ELEMENT_TRANSITION = 4, } export type LayoutAnimationFunction = ( diff --git a/packages/react-native-reanimated/src/createAnimatedComponent/AnimatedComponent.tsx b/packages/react-native-reanimated/src/createAnimatedComponent/AnimatedComponent.tsx index 716a131b10fc..72fec1e76a10 100644 --- a/packages/react-native-reanimated/src/createAnimatedComponent/AnimatedComponent.tsx +++ b/packages/react-native-reanimated/src/createAnimatedComponent/AnimatedComponent.tsx @@ -38,6 +38,7 @@ import JSPropsUpdater from './JSPropsUpdater'; import { NativeEventsManager } from './NativeEventsManager'; import { PropsFilter } from './PropsFilter'; import { filterStyles, flattenArray } from './utils'; +import { LinearTransition } from '../layoutReanimation'; let id = 0; @@ -349,6 +350,18 @@ export default class AnimatedComponent const tag = this.getComponentViewTag(); const { layout, entering, exiting } = this.props; + + if (this.props.sharedTransitionTag) { + updateLayoutAnimations( + tag, + LayoutAnimationType.SHARED_ELEMENT_TRANSITION, + maybeBuild( + LinearTransition.duration(2000), + this.props?.style, + this._displayName + ) + ); + } if (layout || entering || exiting) { if (!SHOULD_BE_USE_WEB) { enableLayoutAnimations(true, false); diff --git a/packages/react-native-reanimated/src/createAnimatedComponent/commonTypes.ts b/packages/react-native-reanimated/src/createAnimatedComponent/commonTypes.ts index 8e0e9e747aa6..ff5b7143b4d0 100644 --- a/packages/react-native-reanimated/src/createAnimatedComponent/commonTypes.ts +++ b/packages/react-native-reanimated/src/createAnimatedComponent/commonTypes.ts @@ -68,6 +68,7 @@ export type AnimatedComponentProps< animatedProps?: Partial>; jestAnimatedValues?: MutableRefObject; animatedStyle?: StyleProps; + sharedTransitionTag?: string; layout?: ( | BaseAnimationBuilder | ILayoutAnimationBuilder diff --git a/packages/react-native-reanimated/src/layoutReanimation/animationsManager.ts b/packages/react-native-reanimated/src/layoutReanimation/animationsManager.ts index 16d22597c3c0..44a2cae1a3d7 100644 --- a/packages/react-native-reanimated/src/layoutReanimation/animationsManager.ts +++ b/packages/react-native-reanimated/src/layoutReanimation/animationsManager.ts @@ -19,6 +19,7 @@ function startObservingProgress( ): void { 'worklet'; sharedValue.addListener(tag + TAG_OFFSET, () => { + console.log('Layout animation progress: ', sharedValue.value, 'tag: ', tag); global._notifyAboutProgress(tag, sharedValue.value); }); } @@ -52,6 +53,7 @@ function createLayoutAnimationManager(): { yogaValues: Partial, config: (arg: Partial) => LayoutAnimation ) { + console.log('Layout animation start: ', tag, type, yogaValues); const style = config(yogaValues); let currentAnimation = style.animations; From 5e6b09aef5c399c1ded0235a4e556c19141893ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Wed, 23 Apr 2025 11:56:22 +0200 Subject: [PATCH 02/90] Multiple transitions --- .../apps/reanimated/examples/EmptyExample.tsx | 5 +- .../rnreanimated/ReanimatedShadowNode.cpp | 1 + .../LayoutAnimationsManager.cpp | 50 +++++++++++++- .../LayoutAnimationsManager.h | 28 +++++++- .../LayoutAnimationsProxy.cpp | 65 +++++++++++++++++-- .../LayoutAnimations/LayoutAnimationsProxy.h | 7 +- .../NativeModules/ReanimatedModuleProxy.cpp | 4 ++ .../src/UpdateLayoutAnimations.ts | 12 +++- .../src/commonTypes.ts | 1 + .../AnimatedComponent.tsx | 4 +- 10 files changed, 164 insertions(+), 13 deletions(-) diff --git a/apps/common-app/src/apps/reanimated/examples/EmptyExample.tsx b/apps/common-app/src/apps/reanimated/examples/EmptyExample.tsx index b6f705565f12..ebcb67252d6e 100644 --- a/apps/common-app/src/apps/reanimated/examples/EmptyExample.tsx +++ b/apps/common-app/src/apps/reanimated/examples/EmptyExample.tsx @@ -33,6 +33,9 @@ function Card({ nextScreen, }: CardProps) { const goNext = (screenName: keyof ParamList) => { + if (nextScreen === 'Screen1') { + navigation.goBack(); + } navigation.navigate(screenName, { title, sharedTransitionTag: transitionTag, @@ -55,7 +58,7 @@ function Card({ function Screen1({ navigation }: NativeStackScreenProps) { return ( - {[...Array(1)].map((_, i) => ( + {[...Array(2)].map((_, i) => ( +#include namespace facebook::react { diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.cpp index c253f0b34f94..32a4ed664f4c 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.cpp @@ -11,7 +11,7 @@ void LayoutAnimationsManager::configureAnimationBatch( const std::vector &layoutAnimationsBatch) { auto lock = std::unique_lock(animationsMutex_); for (auto layoutAnimationConfig : layoutAnimationsBatch) { - const auto &[tag, type, config] = layoutAnimationConfig; + const auto &[tag, type, config, sharedTag] = layoutAnimationConfig; if (type == ENTERING) { enteringAnimationsForNativeID_[tag] = config; continue; @@ -21,6 +21,10 @@ void LayoutAnimationsManager::configureAnimationBatch( } else { getConfigsForType(type)[tag] = config; } + + if (type == SHARED_ELEMENT_TRANSITION){ + sharedTransitionManager_->tagToName_[tag] = sharedTag; + } } } @@ -124,4 +128,48 @@ LayoutAnimationsManager::getConfigsForType(const LayoutAnimationType type) { } } +std::optional SharedTransitionManager::add(const ShadowView& shadowView){ + auto& group = groups_[tagToName_[shadowView.tag]]; + std::optional result; + if (!group.stack_.empty()){ + result = group.tagToView_[group.stack_.top()]; + } + group.stack_.push(shadowView.tag); + group.tagToView_[shadowView.tag] = shadowView; + + return result; +} + +std::optional> SharedTransitionManager::remove(Tag tag){ + auto& group = groups_[tagToName_[tag]]; + std::optional> result; + if (group.stack_.size()>1){ + std::pair p; + p.first = group.tagToView_[group.stack_.top()]; + group.stack_.pop(); + p.second = group.tagToView_[group.stack_.top()]; + result = p; + } else if (group.stack_.size() == 1){ + group.stack_.pop(); + } + + return result; +} + +int SharedTransitionManager::createTransitionContainer(SharedTag sharedTag){ + containers_.push_back(sharedTag); + return containers_.size(); +} + +int SharedTransitionManager::removeTransitionContainer(SharedTag sharedTag){ + for (int i=0; i +#include + #include #include @@ -19,17 +21,39 @@ namespace reanimated { using namespace facebook; using namespace worklets; +using SharedTag = std::string; + +struct SharedTransitionGroup { +// std::string name; +// std::optional current; + std::unordered_map tagToView_; + std::stack stack_; +}; + +struct SharedTransitionManager{ + std::unordered_map groups_; + std::unordered_map tagToName_; + std::vector containers_; + + SharedTransitionGroup getGroupForTag(); + std::optional add(const ShadowView& shadowView); + std::optional> remove(Tag tag); + int createTransitionContainer(SharedTag sharedTag); + int removeTransitionContainer(SharedTag sharedTag); + std::vector> startBackTransition(); +}; struct LayoutAnimationConfig { int tag; LayoutAnimationType type; std::shared_ptr config; + std::string sharedTransitionTag; }; class LayoutAnimationsManager { public: explicit LayoutAnimationsManager(const std::shared_ptr &jsLogger) - : jsLogger_(jsLogger) {} + : jsLogger_(jsLogger), sharedTransitionManager_(std::make_shared()) {} void configureAnimationBatch( const std::vector &layoutAnimationsBatch); void setShouldAnimateExiting(const int tag, const bool value); @@ -46,6 +70,8 @@ class LayoutAnimationsManager { std::unordered_map> &getConfigsForType( const LayoutAnimationType type); + + std::shared_ptr sharedTransitionManager_; private: std::shared_ptr jsLogger_; diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index 0171c3629310..648041e33f2c 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -115,6 +115,9 @@ std::optional LayoutAnimationsProxy::endLayoutAnimation( auto &updateMap = surfaceManager.getUpdateMap(surfaceId); layoutAnimations_.erase(tag); updateMap.erase(tag); + +// auto sharedTag = sharedTransitionManager_->tagToName_[tag]; +// auto index = sharedTransitionManager_->removeTransitionContainer(sharedTag); if (!shouldRemove || !nodeForTag_.contains(tag)) { return {}; @@ -250,6 +253,31 @@ void LayoutAnimationsProxy::handleRemovals( maybeCancelAnimation(node->tag); filteredMutations.push_back(ShadowViewMutation::DeleteMutation( node->mutation.oldChildShadowView)); + + auto tag = node->tag; + + if (layoutAnimationsManager_->hasLayoutAnimation(tag, SHARED_ELEMENT_TRANSITION)){ + auto p = sharedTransitionManager_->remove(tag); + if (p){ + const auto& [before, after] = *p; + ShadowView s = before; + s.tag = myTag; + s.layoutMetrics.frame.origin.y += 100; + filteredMutations.push_back(ShadowViewMutation::CreateMutation(s)); + filteredMutations.push_back(ShadowViewMutation::InsertMutation(1, s, 1)); + layoutAnimationsManager_->getConfigsForType(LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[myTag] = layoutAnimationsManager_->getConfigsForType(LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[before.tag]; + ShadowView copy = after; + copy.tag = myTag; + auto copy2 = before; + copy2.tag = myTag; + copy.layoutMetrics.frame.origin.y += 100; + copy2.layoutMetrics.frame.origin.y += 100; + startSharedTransition(myTag, copy2, copy); + myTag+=2; + continue; + } + } + nodeForTag_.erase(node->tag); #ifdef LAYOUT_ANIMATIONS_LOGS LOG(INFO) << "delete " << node->tag << std::endl; @@ -302,6 +330,7 @@ void LayoutAnimationsProxy::handleUpdatesAndEnterings( } if (layoutAnimationsManager_->hasLayoutAnimation(tag, SHARED_ELEMENT_TRANSITION)){ + auto previousView = sharedTransitionManager_->add(mutation.newChildShadowView); if (previousView){ ShadowView s = *previousView; s.tag = myTag; @@ -309,15 +338,19 @@ void LayoutAnimationsProxy::handleUpdatesAndEnterings( filteredMutations.push_back(ShadowViewMutation::CreateMutation(s)); filteredMutations.push_back(ShadowViewMutation::InsertMutation(1, s, 1)); layoutAnimationsManager_->getConfigsForType(LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[myTag] = layoutAnimationsManager_->getConfigsForType(LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[previousView->tag]; - mutation.newChildShadowView.tag = myTag; + ShadowView copy = mutation.newChildShadowView; + copy.tag = myTag; previousView->tag = myTag; - mutation.newChildShadowView.layoutMetrics.frame.origin.y += 100; + copy.layoutMetrics.frame.origin.y += 100; previousView->layoutMetrics.frame.origin.y += 100; - startSharedTransition(myTag, *previousView, mutation.newChildShadowView); + startSharedTransition(myTag, *previousView, copy); myTag+=2; + std::shared_ptr newView = + cloneViewWithoutOpacity(mutation, propsParserContext); + mutation.newChildShadowView = *newView; + filteredMutations.push_back(mutation); continue; } - previousView = mutation.newChildShadowView; } if (movedViews.contains(tag)) { @@ -536,6 +569,7 @@ bool LayoutAnimationsProxy::startAnimationsRecursively( mutations.push_back(subNode->mutation); toBeRemoved.push_back(subNode); } else if (shouldRemoveSubviewsWithoutAnimations) { + maybeCancelAnimation(subNode->tag); mutations.push_back(subNode->mutation); toBeRemoved.push_back(subNode); @@ -546,6 +580,27 @@ bool LayoutAnimationsProxy::startAnimationsRecursively( #endif mutations.push_back(ShadowViewMutation::DeleteMutation( subNode->mutation.oldChildShadowView)); + if (layoutAnimationsManager_->hasLayoutAnimation(subNode->tag, SHARED_ELEMENT_TRANSITION)){ + auto p = sharedTransitionManager_->remove(subNode->tag); + if (p){ + const auto& [before, after] = *p; + ShadowView s = before; + s.tag = myTag; + s.layoutMetrics.frame.origin.y += 100; + mutations.push_back(ShadowViewMutation::CreateMutation(s)); + mutations.push_back(ShadowViewMutation::InsertMutation(1, s, 1)); + layoutAnimationsManager_->getConfigsForType(LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[myTag] = layoutAnimationsManager_->getConfigsForType(LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[before.tag]; + ShadowView copy = after; + copy.tag = myTag; + auto copy2 = before; + copy2.tag = myTag; + copy.layoutMetrics.frame.origin.y += 100; + copy2.layoutMetrics.frame.origin.y += 100; + startSharedTransition(myTag, copy2, copy); + myTag+=2; + continue; + } + } } else { subNode->state = WAITING; } @@ -573,7 +628,7 @@ bool LayoutAnimationsProxy::startAnimationsRecursively( node->state = ANIMATING; startExitingAnimation(node->tag, node->mutation); } else { - layoutAnimationsManager_->clearLayoutAnimationConfig(node->tag); +// layoutAnimationsManager_->clearLayoutAnimationConfig(node->tag); } return wantAnimateExit; diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h index fda5e4a915fb..c968d05ab393 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h @@ -14,6 +14,7 @@ #include #include #include +#include namespace reanimated { @@ -43,8 +44,9 @@ struct LayoutAnimationsProxy mutable std::unordered_set> deadNodes; mutable std::unordered_map leastRemoved; mutable int myTag = 10001; + std::shared_ptr sharedTransitionManager_; // mutable std::unordered_map< - mutable std::optional previousView; +// mutable std::optional previousView; std::shared_ptr layoutAnimationsManager_; ContextContainer::Shared contextContainer_; SharedComponentDescriptorRegistry componentDescriptorRegistry_; @@ -56,7 +58,8 @@ struct LayoutAnimationsProxy ContextContainer::Shared contextContainer, jsi::Runtime &uiRuntime, const std::shared_ptr uiScheduler) - : layoutAnimationsManager_(layoutAnimationsManager), + : sharedTransitionManager_(layoutAnimationsManager->sharedTransitionManager_), + layoutAnimationsManager_(layoutAnimationsManager), contextContainer_(contextContainer), componentDescriptorRegistry_(componentDescriptorRegistry), uiRuntime_(uiRuntime), diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp index 74cdc43b1cfd..4d6002fa19a4 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp @@ -367,6 +367,10 @@ jsi::Value ReanimatedModuleProxy::configureLayoutAnimationBatch( config, "[Reanimated] Layout animation config must be an object."); } + auto sharedTag = item.getProperty(rt, "sharedTransitionTag"); + if (!sharedTag.isUndefined()){ + batchItem.sharedTransitionTag = sharedTag.asString(rt).utf8(rt); + } } layoutAnimationsManager_->configureAnimationBatch(batch); return jsi::Value::undefined(); diff --git a/packages/react-native-reanimated/src/UpdateLayoutAnimations.ts b/packages/react-native-reanimated/src/UpdateLayoutAnimations.ts index 5c46a8bbe7df..49231acbd3f6 100644 --- a/packages/react-native-reanimated/src/UpdateLayoutAnimations.ts +++ b/packages/react-native-reanimated/src/UpdateLayoutAnimations.ts @@ -56,7 +56,8 @@ export let updateLayoutAnimations: ( viewTag: number, type: LayoutAnimationType, config?: Keyframe | LayoutAnimationFunction, - isUnmounting?: boolean + isUnmounting?: boolean, + sharedTransitionTag?: string ) => void; if (shouldBeUseWeb()) { @@ -65,12 +66,19 @@ if (shouldBeUseWeb()) { }; } else { const updateLayoutAnimationsManager = createUpdateManager(); - updateLayoutAnimations = (viewTag, type, config, isUnmounting) => + updateLayoutAnimations = ( + viewTag, + type, + config, + isUnmounting, + sharedTransitionTag + ) => updateLayoutAnimationsManager.update( { viewTag, type, config: config ? makeShareableCloneRecursive(config) : undefined, + sharedTransitionTag, }, isUnmounting ); diff --git a/packages/react-native-reanimated/src/commonTypes.ts b/packages/react-native-reanimated/src/commonTypes.ts index b93707653b23..bb0d453bf28e 100644 --- a/packages/react-native-reanimated/src/commonTypes.ts +++ b/packages/react-native-reanimated/src/commonTypes.ts @@ -159,6 +159,7 @@ export interface LayoutAnimationBatchItem { viewTag: number; type: LayoutAnimationType; config: ShareableRef | undefined; + sharedTransitionTag?: string; } export type RequiredKeys = T & Required>; diff --git a/packages/react-native-reanimated/src/createAnimatedComponent/AnimatedComponent.tsx b/packages/react-native-reanimated/src/createAnimatedComponent/AnimatedComponent.tsx index 72fec1e76a10..2133bb05b6a8 100644 --- a/packages/react-native-reanimated/src/createAnimatedComponent/AnimatedComponent.tsx +++ b/packages/react-native-reanimated/src/createAnimatedComponent/AnimatedComponent.tsx @@ -359,7 +359,9 @@ export default class AnimatedComponent LinearTransition.duration(2000), this.props?.style, this._displayName - ) + ), + undefined, + this.props.sharedTransitionTag ); } if (layout || entering || exiting) { From 91d6f00b4eebc94217f79d07157fb9e24490904e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Thu, 24 Apr 2025 12:13:18 +0200 Subject: [PATCH 03/90] use LightNodes --- apps/common-app/src/apps/reanimated/App.tsx | 1 + .../apps/reanimated/examples/EmptyExample.tsx | 394 +++++++++++++----- .../apps/reanimated/examples/assets/image.jpg | Bin 0 -> 219089 bytes .../xcschemes/FabricExample.xcscheme | 2 +- .../LayoutAnimationsManager.h | 1 + .../LayoutAnimationsProxy.cpp | 313 +++++++++++--- .../LayoutAnimations/LayoutAnimationsProxy.h | 24 +- 7 files changed, 563 insertions(+), 172 deletions(-) create mode 100644 apps/common-app/src/apps/reanimated/examples/assets/image.jpg diff --git a/apps/common-app/src/apps/reanimated/App.tsx b/apps/common-app/src/apps/reanimated/App.tsx index 2ef8af9cb099..cbb71e117e81 100644 --- a/apps/common-app/src/apps/reanimated/App.tsx +++ b/apps/common-app/src/apps/reanimated/App.tsx @@ -127,6 +127,7 @@ function ItemSeparator() { const Stack = createStack(); const screenOptions = { + headerShown: false, headerLeft: IS_MACOS ? undefined : () => , headerRight: IS_MACOS ? undefined : () => , }; diff --git a/apps/common-app/src/apps/reanimated/examples/EmptyExample.tsx b/apps/common-app/src/apps/reanimated/examples/EmptyExample.tsx index ebcb67252d6e..b0186d0dcbe7 100644 --- a/apps/common-app/src/apps/reanimated/examples/EmptyExample.tsx +++ b/apps/common-app/src/apps/reanimated/examples/EmptyExample.tsx @@ -1,136 +1,324 @@ -import type { - NativeStackNavigationProp, - NativeStackScreenProps, -} from '@react-navigation/native-stack'; +'use strict'; +import type { ParamListBase } from '@react-navigation/native'; +import type { NativeStackScreenProps } from '@react-navigation/native-stack'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; -import * as React from 'react'; -import { StyleSheet, TouchableNativeFeedback, View } from 'react-native'; -import Animated from 'react-native-reanimated'; +import React, { + createContext, + useCallback, + useContext, + useMemo, + useState, +} from 'react'; +import { Button, StyleSheet, Text, View } from 'react-native'; +import Animated, { Layout } from 'react-native-reanimated'; -type ParamList = { - Screen1?: object; - Screen2: { - title: string; - sharedTransitionTag: string; - }; -}; +const Stack = createNativeStackNavigator(); +const Context = createContext({ + theme: true, + disabled: false, + modals: false, + toggleTheme: () => {}, + toggleDisabled: () => {}, + toggleModals: () => {}, +}); -const Stack = createNativeStackNavigator(); +function getTheme(theme: boolean, disabled: boolean) { + const style = theme + ? { backgroundColor: 'purple' } + : { backgroundColor: 'pink' }; + // const config = { duration: 1300 }; + // const customTransition = SharedTransition.custom((values) => { + // 'worklet'; + // return { + // width: withSpring(values.targetWidth, config), + // height: withSpring(values.targetHeight, config), + // originX: withSpring(values.targetOriginX, config), + // originY: withSpring(values.targetOriginY, config), + // borderRadius: withSpring(values.targetBorderRadius, config), + // }; + // }) + // .progressAnimation((values, progress) => { + // 'worklet'; + // const getValue = ( + // progress: number, + // target: number, + // current: number + // ): number => { + // return ( + // (2 * progress * progress - progress) * (target - current) + current + // ); + // }; + // return { + // width: getValue(progress, values.targetWidth, values.currentWidth), + // height: getValue(progress, values.targetHeight, values.currentHeight), + // originX: getValue( + // progress, + // values.targetOriginX, + // values.currentOriginX + // ), + // originY: getValue( + // progress, + // values.targetOriginY, + // values.currentOriginY + // ), + // borderRadius: getValue( + // progress, + // values.targetBorderRadius, + // values.currentBorderRadius + // ), + // }; + // }) + // .defaultTransitionType(SharedTransitionType.ANIMATION); + // const transition = disabled + // ? undefined + // : theme + // ? customTransition + // : new SharedTransition(); -interface CardProps { - navigation: NativeStackNavigationProp; - title: string; - transitionTag: string; - isOpen?: boolean; - nextScreen: keyof ParamList; + return { style }; } -function Card({ - navigation, - title, - transitionTag, - isOpen = false, - nextScreen, -}: CardProps) { - const goNext = (screenName: keyof ParamList) => { - if (nextScreen === 'Screen1') { - navigation.goBack(); - } - navigation.navigate(screenName, { - title, - sharedTransitionTag: transitionTag, - }); - }; - +function Screen1({ navigation }: NativeStackScreenProps) { + const { theme, disabled, modals, toggleTheme, toggleDisabled, toggleModals } = + useContext(Context); + const { style } = useMemo(() => getTheme(theme, disabled), [theme, disabled]); return ( - { - goNext(nextScreen); - }}> + + Current theme: {theme ? 1 : 2} + ); } @@ -188,6 +196,12 @@ const styles = StyleSheet.create({ fontSize: 16, color: 'black', }, + marker: { + fontSize: 20, + color: 'black', + alignSelf: 'flex-end', + marginLeft: 'auto', + }, visitedItem: { backgroundColor: '#e6f0f7', }, diff --git a/apps/common-app/src/apps/reanimated/examples/index.ts b/apps/common-app/src/apps/reanimated/examples/index.ts index ffa5b1d3982a..f1984c848f59 100644 --- a/apps/common-app/src/apps/reanimated/examples/index.ts +++ b/apps/common-app/src/apps/reanimated/examples/index.ts @@ -160,6 +160,10 @@ interface Example { icon?: string; title: string; screen: React.FC; + shouldWork?: { + ios: boolean; + android: boolean; + }; missingOnFabric?: boolean; } @@ -602,16 +606,28 @@ export const EXAMPLES: Record = { icon: '🙆‍♂️', title: 'Profiles', screen: ProfilesExample, + shouldWork: { + ios: true, + android: false, // I sometimes see a flicker when the transition starts to go back + }, }, ProgressTransitionExample: { icon: '☕', title: 'Progress transition', screen: ProgressTransitionExample, + shouldWork: { + ios: true, + android: true, + }, }, GalleryExample: { icon: '🇮🇹', title: 'Gallery', screen: GalleryExample, + shouldWork: { + ios: true, + android: true, + }, }, // Old examples @@ -823,42 +839,82 @@ export const EXAMPLES: Record = { CardExample: { title: '[SET] Card', screen: CardExample, + shouldWork: { + ios: true, + android: true, + }, }, CustomTransitionExample: { title: '[SET] Custom transition', screen: CustomTransitionExample, + shouldWork: { + ios: true, + android: true, + }, }, LayoutAnimationExample: { title: '[SET] Layout Animation', screen: LayoutAnimationExample, + shouldWork: { + ios: true, + android: true, + }, }, ManyScreensExample: { title: '[SET] Many screens', screen: ManyScreensExample, + shouldWork: { + ios: true, + android: true, + }, }, ManyTagsExample: { title: '[SET] Many tags', screen: ManyTagsExample, + shouldWork: { + ios: true, + android: true, + }, }, NestedStacksExample: { title: '[SET] Nested stacks', screen: NestedStacksExample, + shouldWork: { + ios: true, + android: false, // borderWidth issue + }, }, ModalsExample: { title: '[SET] Modals', screen: ModalsExample, + shouldWork: { + ios: false, // broken header height + android: true, + }, }, FlatListExample: { title: '[SET] FlatList', screen: FlatListExample, + shouldWork: { + ios: true, + android: true, + }, }, ImageStackExample: { title: '[SET] Image Stack', screen: ImageStackExample, + shouldWork: { + ios: true, + android: true, + }, }, RestoreStateExample: { title: '[SET] Restore State', screen: RestoreStateExample, + shouldWork: { + ios: true, + android: true, + }, }, DuplicateTagsExample: { title: '[SET] Duplicate Tags', @@ -871,21 +927,42 @@ export const EXAMPLES: Record = { TransitionRestartExample: { title: '[SET] Transition Restart', screen: TransitionRestartExample, + shouldWork: { + ios: false, // goes too far up for some reason? + android: true, + }, }, ChangeThemeSharedExample: { title: '[SET] Change theme', screen: ChangeThemeSharedExample, + shouldWork: { + ios: false, // s2 -> change theme -> go back (progress) will have wrong target + android: false, // broken borderRadius for some reason + }, }, NestedRotationSharedExample: { title: '[SET] Nested Transforms', screen: NestedRotationExample, + shouldWork: { + ios: false, // broken for modals + android: false, // broken transform, I think due to skew + }, }, BorderRadiiExample: { title: '[SET] Border Radii', screen: BorderRadiiExample, + shouldWork: { + ios: false, // broken on back gesture + android: true, + }, }, TabNavigatorExample: { title: '[SET] Tab Navigator', screen: TabNavigatorExample, + shouldWork: { + // not implemented + ios: false, + android: false, + }, }, } as const; From bbf9f3c0ffb228f112cc41f86ef1608665d1e1d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Tue, 16 Sep 2025 17:40:38 +0200 Subject: [PATCH 38/90] Allow header back button to trigger progress transition --- .../cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index a8112e5a4013..26438bb233c5 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -785,7 +785,7 @@ isAndroid = true; isAndroid = false; #endif // TODO: this new approach causes all back transitions to be progress transitions - if (isSwiping && !isClosing && !isGoingForward && !isAndroid){ + if (!isClosing && !isGoingForward && !isAndroid){ transitionProgress_ = progress; if (transitionState_ == NONE && progress < 1){ transitionState_ = START; From ea94c0fb05b2a393b5cef6ea1142e86c37b2250e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Thu, 25 Sep 2025 16:49:55 +0200 Subject: [PATCH 39/90] Fix borderWidth default --- .../Common/cpp/reanimated/LayoutAnimations/PropsDiffer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/PropsDiffer.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/PropsDiffer.cpp index 06581d4042f5..06a0d8623d6a 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/PropsDiffer.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/PropsDiffer.cpp @@ -591,13 +591,13 @@ void PropsDiffer::diffBorderWidth( if (sourceValue.has_value()) { sourceValues_.setProperty(rt, name, source); } else { - sourceValues_.setProperty(rt, name, react::toString(defaultSourceWidth)); + sourceValues_.setProperty(rt, name, defaultSourceWidth); } if (targetValue.has_value()) { targetValues_.setProperty(rt, name, target); } else { - targetValues_.setProperty(rt, name, react::toString(defaultTargetWidth)); + targetValues_.setProperty(rt, name, defaultTargetWidth); } } } From c93fb086efb809bbeff9edd5a5530040816cd907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Thu, 25 Sep 2025 16:54:42 +0200 Subject: [PATCH 40/90] Mark nested stack as fixed --- apps/common-app/src/apps/reanimated/examples/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/common-app/src/apps/reanimated/examples/index.ts b/apps/common-app/src/apps/reanimated/examples/index.ts index f1984c848f59..ff42c8f3667e 100644 --- a/apps/common-app/src/apps/reanimated/examples/index.ts +++ b/apps/common-app/src/apps/reanimated/examples/index.ts @@ -881,7 +881,7 @@ export const EXAMPLES: Record = { screen: NestedStacksExample, shouldWork: { ios: true, - android: false, // borderWidth issue + android: true, }, }, ModalsExample: { From f90cfd57d8e69ee53b8f838a2111f09d7b106b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Fri, 26 Sep 2025 11:09:59 +0200 Subject: [PATCH 41/90] fix for missing props in created containers --- .../src/apps/reanimated/examples/index.ts | 2 +- .../LayoutAnimationsProxy.cpp | 29 +++++++++++++++---- .../LayoutAnimations/LayoutAnimationsProxy.h | 2 +- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/apps/common-app/src/apps/reanimated/examples/index.ts b/apps/common-app/src/apps/reanimated/examples/index.ts index ff42c8f3667e..93662741b271 100644 --- a/apps/common-app/src/apps/reanimated/examples/index.ts +++ b/apps/common-app/src/apps/reanimated/examples/index.ts @@ -937,7 +937,7 @@ export const EXAMPLES: Record = { screen: ChangeThemeSharedExample, shouldWork: { ios: false, // s2 -> change theme -> go back (progress) will have wrong target - android: false, // broken borderRadius for some reason + android: true, }, }, NestedRotationSharedExample: { diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index 26438bb233c5..d37f732b0f13 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -40,11 +40,11 @@ std::optional LayoutAnimationsProxy::pullTransaction(Surfac bool isInTransition = transitionState_; if (isInTransition){ - updateLightTree(mutations, filteredMutations); + updateLightTree(propsParserContext, mutations, filteredMutations); handleProgressTransition(filteredMutations, mutations, propsParserContext, surfaceId); } else if (!synchronized_){ auto actualTop = topScreen[surfaceId]; - updateLightTree(mutations, filteredMutations); + updateLightTree(propsParserContext, mutations, filteredMutations); auto reactTop = findTopScreen(lightNodes_[surfaceId]); if (reactTop->current.tag == actualTop->current.tag){ synchronized_ = true; @@ -56,7 +56,7 @@ std::optional LayoutAnimationsProxy::pullTransaction(Surfac findSharedElementsOnScreen(beforeTopScreen, 0); } - updateLightTree(mutations, filteredMutations); + updateLightTree(propsParserContext, mutations, filteredMutations); root = lightNodes_[surfaceId]; auto afterTopScreen = findTopScreen(root); @@ -446,7 +446,7 @@ void LayoutAnimationsProxy::handleProgressTransition(ShadowViewMutationList &fil } } -void LayoutAnimationsProxy::updateLightTree(const ShadowViewMutationList &mutations, ShadowViewMutationList& filteredMutations) const { +void LayoutAnimationsProxy::updateLightTree(const PropsParserContext& propsParserContext, const ShadowViewMutationList &mutations, ShadowViewMutationList& filteredMutations) const { std::unordered_set moved, deleted; for (auto it = mutations.rbegin(); it != mutations.rend(); it++){ @@ -482,7 +482,21 @@ void LayoutAnimationsProxy::updateLightTree(const ShadowViewMutationList &mutati node = std::make_shared(); } node->previous = mutation.oldChildShadowView; + #ifdef ANDROID + if (node->current.props) { + // on android rawProps are used to store the diffed props + // so we need to merge them + // this should soon be replaced in RN with Props 2.0 (the diffing will be done at the end of the pipeline) + auto& currentRawProps = node->current.props->rawProps; + auto mergedRawProps = folly::dynamic::merge(currentRawProps, mutation.newChildShadowView.props->rawProps); + node->current = mutation.newChildShadowView; + node->current.props = getComponentDescriptorForShadowView(node->current).cloneProps(propsParserContext, mutation.newChildShadowView.props, RawProps(mergedRawProps)); + } else { + node->current = mutation.newChildShadowView; + } + #else node->current = mutation.newChildShadowView; + #endif auto tag = mutation.newChildShadowView.tag; if (layoutAnimationsManager_->hasLayoutAnimation(tag, LAYOUT)){ layout_.push_back(node); @@ -595,6 +609,10 @@ void LayoutAnimationsProxy::handleSharedTransitionsStart(const LightNode::Unshar if (shouldCreateContainer){ auto& root = lightNodes_[surfaceId]; ShadowView s = before; + auto beforeViewProps = std::const_pointer_cast(std::static_pointer_cast(s.props)); + auto afterViewProps = std::const_pointer_cast(std::static_pointer_cast(after.props)); + + SYSLOG(INFO) << "(dupa)" << "before: " << beforeViewProps->borderRadii.all.value_or(ValueUnit{}).value << " " <rawProps << ", after: " << afterViewProps->borderRadii.all.value_or(ValueUnit{}).value << " " << afterViewProps->rawProps; s.tag = myTag; auto newProps = getComponentDescriptorForShadowView(s).cloneProps(propsParserContext, s.props, {}); auto viewProps = std::const_pointer_cast(std::static_pointer_cast(newProps)); @@ -1345,8 +1363,9 @@ std::shared_ptr LayoutAnimationsProxy::cloneViewWithoutOpacity( const PropsParserContext &propsParserContext) const { auto newView = std::make_shared(mutation.newChildShadowView); folly::dynamic opacity = folly::dynamic::object("opacity", 0); +// opacity = ; auto newProps = getComponentDescriptorForShadowView(*newView).cloneProps( - propsParserContext, newView->props, RawProps(opacity)); + propsParserContext, newView->props, RawProps(folly::dynamic::merge(opacity, newView->props->rawProps))); newView->props = newProps; return newView; } diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h index 5bc26ba60478..812f31104039 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h @@ -95,7 +95,7 @@ struct LayoutAnimationsProxy const; void handleProgressTransition(ShadowViewMutationList &filteredMutations, const ShadowViewMutationList &mutations, const PropsParserContext &propsParserContext, SurfaceId surfaceId) const; - void updateLightTree(const ShadowViewMutationList &mutations, ShadowViewMutationList& filteredMutations) const; + void updateLightTree(const PropsParserContext& propsParserContext, const ShadowViewMutationList &mutations, ShadowViewMutationList& filteredMutations) const; void handleSharedTransitionsStart(const LightNode::Unshared &afterTopScreen, const LightNode::Unshared &beforeTopScreen, ShadowViewMutationList &filteredMutations, const ShadowViewMutationList &mutations, const PropsParserContext &propsParserContext, SurfaceId surfaceId) const; From 976a8e4aa6a90c672d9805427758f98ad522afa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Fri, 26 Sep 2025 11:23:58 +0200 Subject: [PATCH 42/90] oopsie --- .../cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index d37f732b0f13..057557628dc1 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -612,7 +612,6 @@ void LayoutAnimationsProxy::handleSharedTransitionsStart(const LightNode::Unshar auto beforeViewProps = std::const_pointer_cast(std::static_pointer_cast(s.props)); auto afterViewProps = std::const_pointer_cast(std::static_pointer_cast(after.props)); - SYSLOG(INFO) << "(dupa)" << "before: " << beforeViewProps->borderRadii.all.value_or(ValueUnit{}).value << " " <rawProps << ", after: " << afterViewProps->borderRadii.all.value_or(ValueUnit{}).value << " " << afterViewProps->rawProps; s.tag = myTag; auto newProps = getComponentDescriptorForShadowView(s).cloneProps(propsParserContext, s.props, {}); auto viewProps = std::const_pointer_cast(std::static_pointer_cast(newProps)); From c00966179799e4afa475bed758f588709f86542c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Fri, 26 Sep 2025 13:54:05 +0200 Subject: [PATCH 43/90] Fix Profiles blinking on android --- .../src/apps/reanimated/examples/index.ts | 2 +- .../LayoutAnimationsProxy.cpp | 20 +++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/apps/common-app/src/apps/reanimated/examples/index.ts b/apps/common-app/src/apps/reanimated/examples/index.ts index 93662741b271..02594e5203fb 100644 --- a/apps/common-app/src/apps/reanimated/examples/index.ts +++ b/apps/common-app/src/apps/reanimated/examples/index.ts @@ -608,7 +608,7 @@ export const EXAMPLES: Record = { screen: ProfilesExample, shouldWork: { ios: true, - android: false, // I sometimes see a flicker when the transition starts to go back + android: true, }, }, ProgressTransitionExample: { diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index 057557628dc1..8d69e0253403 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -613,10 +613,22 @@ void LayoutAnimationsProxy::handleSharedTransitionsStart(const LightNode::Unshar auto afterViewProps = std::const_pointer_cast(std::static_pointer_cast(after.props)); s.tag = myTag; - auto newProps = getComponentDescriptorForShadowView(s).cloneProps(propsParserContext, s.props, {}); - auto viewProps = std::const_pointer_cast(std::static_pointer_cast(newProps)); - viewProps->transform = transformForNode_[before.tag]; - s.props = newProps; + + if (transformForNode_.contains(before.tag)){ +#ifdef ANDROID + + auto array = folly::dynamic::array(folly::dynamic::object("matrix", transformForNode_[before.tag].operator folly::dynamic())); + folly::dynamic newTransformDynamic = folly::dynamic::object("transform", array); + auto newRawProps = folly::dynamic::merge(s.props->rawProps, newTransformDynamic); + auto newProps = getComponentDescriptorForShadowView(s).cloneProps(propsParserContext, s.props, RawProps(newRawProps)); + auto viewProps = std::const_pointer_cast(std::static_pointer_cast(newProps)); +#else + auto newProps = getComponentDescriptorForShadowView(s).cloneProps(propsParserContext, s.props, {}); + auto viewProps = std::const_pointer_cast(std::static_pointer_cast(newProps)); + viewProps->transform = transformForNode_[before.tag]; +#endif + s.props = newProps; + } filteredMutations.push_back(ShadowViewMutation::CreateMutation(s)); filteredMutations.push_back(ShadowViewMutation::InsertMutation(surfaceId, s, root->children.size())); filteredMutations.push_back(ShadowViewMutation::UpdateMutation(after, after, afterParentTag)); From 587779aa0766fc418de60276876e1f3a60946a38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Fri, 26 Sep 2025 14:11:31 +0200 Subject: [PATCH 44/90] oopsie --- .../cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index 8d69e0253403..897e14d3a891 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -1374,9 +1374,8 @@ std::shared_ptr LayoutAnimationsProxy::cloneViewWithoutOpacity( const PropsParserContext &propsParserContext) const { auto newView = std::make_shared(mutation.newChildShadowView); folly::dynamic opacity = folly::dynamic::object("opacity", 0); -// opacity = ; auto newProps = getComponentDescriptorForShadowView(*newView).cloneProps( - propsParserContext, newView->props, RawProps(folly::dynamic::merge(opacity, newView->props->rawProps))); + propsParserContext, newView->props, RawProps(opacity)); newView->props = newProps; return newView; } From 6cecaf312340af8fa4ed42ab08ddb0e321684cfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Fri, 26 Sep 2025 14:50:42 +0200 Subject: [PATCH 45/90] Profiles flicker ios --- .../reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index 897e14d3a891..9bbc5d7e297e 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -260,7 +260,11 @@ void LayoutAnimationsProxy::parseParentTransforms(const LightNode::Unshared &nod combinedMatrix.operations.clear(); } if (parentHasTransform) { - transformForNode_[node->current.tag] = combinedMatrix; + transformForNode_[node->current.tag] = Transform::FromTransformOperation( + react::TransformOperation(TransformOperationType::Arbitrary), + {}, + combinedMatrix + ); } } From ef77854aa8ea177097e2622391e5e863c884a374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Fri, 26 Sep 2025 14:55:58 +0200 Subject: [PATCH 46/90] Mark examples --- apps/common-app/src/apps/reanimated/examples/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/common-app/src/apps/reanimated/examples/index.ts b/apps/common-app/src/apps/reanimated/examples/index.ts index 02594e5203fb..be0639600582 100644 --- a/apps/common-app/src/apps/reanimated/examples/index.ts +++ b/apps/common-app/src/apps/reanimated/examples/index.ts @@ -864,7 +864,7 @@ export const EXAMPLES: Record = { title: '[SET] Many screens', screen: ManyScreensExample, shouldWork: { - ios: true, + ios: false, // header height issues on iOS 26 android: true, }, }, From f765d34f57054acfa9945b181b8bf5f7ff870bf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Fri, 26 Sep 2025 14:57:29 +0200 Subject: [PATCH 47/90] format --- .../LayoutAnimationsManager.cpp | 9 +- .../LayoutAnimationsManager.h | 32 +- .../LayoutAnimationsProxy.cpp | 1016 ++++++++++------- .../LayoutAnimations/LayoutAnimationsProxy.h | 162 ++- .../LayoutAnimations/LayoutAnimationsUtils.h | 3 +- .../LayoutAnimations/PropsDiffer.cpp | 11 +- .../NativeModules/ReanimatedModuleProxy.cpp | 24 +- .../src/layoutReanimation/SharedTransition.ts | 37 +- 8 files changed, 757 insertions(+), 537 deletions(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.cpp index fc4e44f252d1..57877eba13f7 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.cpp @@ -15,7 +15,7 @@ void LayoutAnimationsManager::configureAnimationBatch( enteringAnimationsForNativeID_[tag] = config; continue; } - if (type == SHARED_ELEMENT_TRANSITION){ + if (type == SHARED_ELEMENT_TRANSITION) { sharedTransitionsForNativeID_[tag] = config; sharedTransitionManager_->nativeIDToName_[tag] = sharedTag; continue; @@ -110,11 +110,12 @@ void LayoutAnimationsManager::transferConfigFromNativeID( enteringAnimations_.insert_or_assign(tag, config); } enteringAnimationsForNativeID_.erase(nativeId); - + auto setConfig = sharedTransitionsForNativeID_[nativeId]; - if (setConfig){ + if (setConfig) { sharedTransitions_.insert_or_assign(tag, setConfig); - sharedTransitionManager_->tagToName_[tag] = sharedTransitionManager_->nativeIDToName_[nativeId]; + sharedTransitionManager_->tagToName_[tag] = + sharedTransitionManager_->nativeIDToName_[nativeId]; } sharedTransitionsForNativeID_.erase(nativeId); } diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.h index 5d4dac53b545..9142f2d0f987 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.h @@ -1,8 +1,7 @@ #pragma once -#include #include - +#include #include #include @@ -12,11 +11,11 @@ #include #include #include +#include #include #include #include #include -#include namespace reanimated { @@ -25,26 +24,26 @@ using namespace worklets; using SharedTag = std::string; struct SharedTransitionGroup { -// std::string name; -// std::optional current; + // std::string name; + // std::optional current; std::unordered_map tagToView_; Tag fakeTag = -1; }; struct Transition { ShadowView snapshot[2]; - Tag parentTag[2] = {0,0}; + Tag parentTag[2] = {0, 0}; }; -struct SharedTransitionManager{ +struct SharedTransitionManager { std::unordered_map groups_; std::unordered_map tagToName_; std::unordered_map nativeIDToName_; - -// SharedTransitionGroup getGroupForTag(); -// int createTransitionContainer(SharedTag sharedTag); -// int removeTransitionContainer(SharedTag sharedTag); -// std::vector> startBackTransition(); + + // SharedTransitionGroup getGroupForTag(); + // int createTransitionContainer(SharedTag sharedTag); + // int removeTransitionContainer(SharedTag sharedTag); + // std::vector> startBackTransition(); }; using TransitionMap = std::unordered_map; @@ -60,7 +59,8 @@ struct LayoutAnimationConfig { class LayoutAnimationsManager { public: explicit LayoutAnimationsManager(const std::shared_ptr &jsLogger) - : sharedTransitionManager_(std::make_shared()), jsLogger_(jsLogger) {} + : sharedTransitionManager_(std::make_shared()), + jsLogger_(jsLogger) {} void configureAnimationBatch( const std::vector &layoutAnimationsBatch); void setShouldAnimateExiting(const int tag, const bool value); @@ -75,13 +75,13 @@ class LayoutAnimationsManager { void cancelLayoutAnimation(jsi::Runtime &rt, const int tag) const; void transferConfigFromNativeID(const int nativeId, const int tag); -// private: + // private: std::unordered_map> &getConfigsForType( const LayoutAnimationType type); - + std::shared_ptr sharedTransitionManager_; -private: + private: std::shared_ptr jsLogger_; std::unordered_map> diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index 9bbc5d7e297e..4ba5989d5e30 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -1,30 +1,34 @@ #include -#include #include +#include #ifndef ANDROID #include #endif +#include #include +#include +#include #include #include -#include -#include -#include #ifdef ANDROID #undef LOG #define LOG SYSLOG #endif +#include +#include #include #include -#include -#include using ScrollState = ConcreteState; namespace reanimated { -std::optional LayoutAnimationsProxy::pullTransaction(SurfaceId surfaceId, MountingTransaction::Number transactionNumber, const TransactionTelemetry &telemetry, ShadowViewMutationList mutations) const { +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() << " " @@ -38,58 +42,72 @@ std::optional LayoutAnimationsProxy::pullTransaction(Surfac std::vector> roots; std::unordered_map movedViews; bool isInTransition = transitionState_; - - if (isInTransition){ + + if (isInTransition) { updateLightTree(propsParserContext, mutations, filteredMutations); - handleProgressTransition(filteredMutations, mutations, propsParserContext, surfaceId); - } else if (!synchronized_){ + handleProgressTransition( + filteredMutations, mutations, propsParserContext, surfaceId); + } else if (!synchronized_) { auto actualTop = topScreen[surfaceId]; updateLightTree(propsParserContext, mutations, filteredMutations); auto reactTop = findTopScreen(lightNodes_[surfaceId]); - if (reactTop->current.tag == actualTop->current.tag){ + if (reactTop->current.tag == actualTop->current.tag) { synchronized_ = true; } } else { auto root = lightNodes_[surfaceId]; auto beforeTopScreen = topScreen[surfaceId]; - if (beforeTopScreen){ + if (beforeTopScreen) { findSharedElementsOnScreen(beforeTopScreen, 0); } - + updateLightTree(propsParserContext, mutations, filteredMutations); - + root = lightNodes_[surfaceId]; auto afterTopScreen = findTopScreen(root); topScreen[surfaceId] = afterTopScreen; - if (afterTopScreen){ + if (afterTopScreen) { findSharedElementsOnScreen(afterTopScreen, 1); } - bool shouldTransitionStart = beforeTopScreen && afterTopScreen && beforeTopScreen->current.tag != afterTopScreen->current.tag; - - if (shouldTransitionStart){ + bool shouldTransitionStart = beforeTopScreen && afterTopScreen && + beforeTopScreen->current.tag != afterTopScreen->current.tag; + + if (shouldTransitionStart) { std::vector temp; hideTransitioningViews(0, temp, propsParserContext); - temp.insert(temp.end(), filteredMutations.begin(), filteredMutations.end()); + temp.insert( + temp.end(), filteredMutations.begin(), filteredMutations.end()); hideTransitioningViews(1, temp, propsParserContext); std::swap(filteredMutations, temp); } - - handleSharedTransitionsStart(afterTopScreen, beforeTopScreen, filteredMutations, mutations, propsParserContext, surfaceId); - - for (auto& node: entering_){ - startEnteringAnimation(node->current.tag, ShadowViewMutation::InsertMutation(node->parent.lock()->current.tag, node->current, -1)); + + handleSharedTransitionsStart( + afterTopScreen, + beforeTopScreen, + filteredMutations, + mutations, + propsParserContext, + surfaceId); + + for (auto &node : entering_) { + startEnteringAnimation( + node->current.tag, + ShadowViewMutation::InsertMutation( + node->parent.lock()->current.tag, node->current, -1)); } - for (auto& node: layout_){ - startLayoutAnimation(node->current.tag, ShadowViewMutation::UpdateMutation(node->previous, node->current, node->parent.lock()->current.tag)); + for (auto &node : layout_) { + startLayoutAnimation( + node->current.tag, + ShadowViewMutation::UpdateMutation( + node->previous, node->current, node->parent.lock()->current.tag)); } entering_.clear(); layout_.clear(); - + handleRemovals(filteredMutations, exiting_); exiting_.clear(); - } - + cleanupSharedTransitions(filteredMutations, propsParserContext, surfaceId); addOngoingAnimations(surfaceId, filteredMutations); @@ -100,113 +118,130 @@ std::optional LayoutAnimationsProxy::pullTransaction(Surfac updateMap.erase(tag); } finishedAnimationTags_.clear(); - + transitionMap_.clear(); transitions_.clear(); - + return MountingTransaction{ surfaceId, transactionNumber, std::move(filteredMutations), telemetry}; } -Tag LayoutAnimationsProxy::findVisible(std::shared_ptr node, int& count) const{ -// auto group = sharedTransitionManager_->groups_[sharedTransitionManager_->tagToName_[node->current.tag]]; -// while (node != nullptr){ -// if (!strcmp(node->current.componentName, "RNSScreenStack")){ -// -// } -// node = node->parent.lock(); -// } +Tag LayoutAnimationsProxy::findVisible( + std::shared_ptr node, + int &count) const { + // auto group = + // sharedTransitionManager_->groups_[sharedTransitionManager_->tagToName_[node->current.tag]]; + // while (node != nullptr){ + // if (!strcmp(node->current.componentName, "RNSScreenStack")){ + // + // } + // node = node->parent.lock(); + // } int c = count; - if (!strcmp(node->current.componentName, "RNSScreen")){ - LOG(INFO) << c <<" begin screen tag: " << node->current.tag<current.componentName, "RNSScreen")) { + LOG(INFO) << c << " begin screen tag: " << node->current.tag << std::endl; count++; } - for (auto& child: node->children){ + for (auto &child : node->children) { findVisible(child, count); } - if (!strcmp(node->current.componentName, "RNSScreen")){ - LOG(INFO) << c <<" end screen" <current.componentName, "RNSScreen")) { + LOG(INFO) << c << " end screen" << std::endl; } return -1; } -LightNode::Unshared LayoutAnimationsProxy::findTopScreen(LightNode::Unshared node) const{ +LightNode::Unshared LayoutAnimationsProxy::findTopScreen( + LightNode::Unshared node) const { LightNode::Unshared result = nullptr; - if (!node->current.componentName){ + if (!node->current.componentName) { return result; } - if (!(strcmp(node->current.componentName, "RNSScreen")) || !(strcmp(node->current.componentName, "RNSModalScreen"))){ - bool isActive = false; + if (!(strcmp(node->current.componentName, "RNSScreen")) || + !(strcmp(node->current.componentName, "RNSModalScreen"))) { + bool isActive = false; #ifdef ANDROID - // TODO: this looks like a RNSScreens bug - sometimes there is no active screen at a deeper level, when going back -// float f = node->current.props->rawProps.getDefault("activityState", 0).asDouble(); -// isActive = f == 2.0f; - isActive = true; + // TODO: this looks like a RNSScreens bug - sometimes there is no active + // screen at a deeper level, when going back + // float f = node->current.props->rawProps.getDefault("activityState", + // 0).asDouble(); isActive = f == 2.0f; + isActive = true; #else - isActive = std::static_pointer_cast(node->current.props)->activityState == 2.0f; + isActive = + std::static_pointer_cast(node->current.props) + ->activityState == 2.0f; #endif - if (isActive) { - result = node; - } + if (isActive) { + result = node; + } } - - for (auto it = node->children.rbegin(); it != node->children.rend(); it++){ + + for (auto it = node->children.rbegin(); it != node->children.rend(); it++) { auto t = findTopScreen(*it); - if (t){ + if (t) { return t; } } - + return result; } -void LayoutAnimationsProxy::findSharedElementsOnScreen(const LightNode::Unshared& node, int index) const{ - if (sharedTransitionManager_->tagToName_.contains(node->current.tag)){ +void LayoutAnimationsProxy::findSharedElementsOnScreen( + const LightNode::Unshared &node, + int index) const { + if (sharedTransitionManager_->tagToName_.contains(node->current.tag)) { ShadowView copy = node->current; std::vector absolutePositions; absolutePositions = getAbsolutePositionsForRootPathView(node); copy.layoutMetrics.frame.origin = absolutePositions[0]; auto sharedTag = sharedTransitionManager_->tagToName_[node->current.tag]; - auto& transition = transitionMap_[sharedTag]; + auto &transition = transitionMap_[sharedTag]; transition.snapshot[index] = copy; transition.parentTag[index] = node->parent.lock()->current.tag; parseParentTransforms(node, absolutePositions); - if (transition.parentTag[0] && transition.parentTag[1]){ + if (transition.parentTag[0] && transition.parentTag[1]) { transitions_.push_back({sharedTag, transition}); - } else if (transition.parentTag[1]){ + } else if (transition.parentTag[1]) { // TODO: this is too eager tagsToRestore_.push_back(transition.snapshot[1].tag); } } - for (auto& child: node->children){ + for (auto &child : node->children) { findSharedElementsOnScreen(child, index); } } -//struct S{ -// const react::Size frameSize{}; -// react::Point contentOffset; -//}; +// struct S{ +// const react::Size frameSize{}; +// react::Point contentOffset; +// }; -std::vector LayoutAnimationsProxy::getAbsolutePositionsForRootPathView(const LightNode::Unshared &node) const { +std::vector +LayoutAnimationsProxy::getAbsolutePositionsForRootPathView( + const LightNode::Unshared &node) const { std::vector viewsAbsolutePositions; auto currentNode = node; - while (currentNode){ + while (currentNode) { react::Point viewPosition; - if (!strcmp(currentNode->current.componentName, "ScrollView")){ - auto state = std::static_pointer_cast(currentNode->current.state); + if (!strcmp(currentNode->current.componentName, "ScrollView")) { + auto state = std::static_pointer_cast( + currentNode->current.state); auto data = state->getData(); -// LOG(INFO) << node->current.tag << " content offset:" << data.contentOffset.x << " " << data.contentOffset.y; + // LOG(INFO) << node->current.tag << " content offset:" << + // data.contentOffset.x << " " << data.contentOffset.y; viewPosition -= data.contentOffset; } - if (!strcmp(currentNode->current.componentName, "RNSScreen") && currentNode->children.size()>=2){ + if (!strcmp(currentNode->current.componentName, "RNSScreen") && + currentNode->children.size() >= 2) { const auto &parent = currentNode->parent.lock(); - if (parent){ - float headerHeight = parent->current.layoutMetrics.frame.size.height - currentNode->current.layoutMetrics.frame.size.height; + if (parent) { + float headerHeight = parent->current.layoutMetrics.frame.size.height - + currentNode->current.layoutMetrics.frame.size.height; viewPosition.y += headerHeight; -// auto state = std::reinterpret_pointer_cast>(currentNode->current.state); -// LOG(INFO) <<"state: " <getData().contentOffset.y; -// viewPosition.y += state->getData().contentOffset.y; + // auto state = std::reinterpret_pointer_cast>(currentNode->current.state); LOG(INFO) + // <<"state: " <getData().contentOffset.y; viewPosition.y + // += state->getData().contentOffset.y; } } viewPosition += currentNode->current.layoutMetrics.frame.origin; @@ -219,24 +254,33 @@ std::vector LayoutAnimationsProxy::getAbsolutePositionsForRootPath return viewsAbsolutePositions; } -void LayoutAnimationsProxy::parseParentTransforms(const LightNode::Unshared &node, const std::vector &absolutePositions) const { +void LayoutAnimationsProxy::parseParentTransforms( + const LightNode::Unshared &node, + const std::vector &absolutePositions) const { std::vector> transforms; auto currentNode = node; while (currentNode) { - const auto& props = static_cast(*currentNode->current.props); + const auto &props = + static_cast(*currentNode->current.props); auto origin = props.transformOrigin; const auto &viewSize = currentNode->current.layoutMetrics.frame.size; if (origin.xy[0].unit == facebook::react::UnitType::Percent) { - origin.xy[0] = { static_cast(viewSize.width * origin.xy[0].value / 100), UnitType::Point }; + origin.xy[0] = { + static_cast(viewSize.width * origin.xy[0].value / 100), + UnitType::Point}; } else if (origin.xy[0].unit == facebook::react::UnitType::Undefined) { - origin.xy[0] = { static_cast(viewSize.width*0.5), UnitType::Point}; + origin.xy[0] = { + static_cast(viewSize.width * 0.5), UnitType::Point}; } if (origin.xy[1].unit == facebook::react::UnitType::Percent) { - origin.xy[1] = { static_cast(viewSize.height * origin.xy[1].value / 100), UnitType::Point }; + origin.xy[1] = { + static_cast(viewSize.height * origin.xy[1].value / 100), + UnitType::Point}; } else if (origin.xy[1].unit == facebook::react::UnitType::Undefined) { - origin.xy[1] = { static_cast(viewSize.height*0.5), UnitType::Point}; + origin.xy[1] = { + static_cast(viewSize.height * 0.5), UnitType::Point}; } - transforms.emplace_back( props.transform, origin ); + transforms.emplace_back(props.transform, origin); currentNode = currentNode->parent.lock(); } @@ -244,84 +288,92 @@ void LayoutAnimationsProxy::parseParentTransforms(const LightNode::Unshared &nod Transform combinedMatrix; bool parentHasTransform = false; for (long int i = transforms.size() - 1; i >= 0; --i) { - auto& [transform, transformOrigin] = transforms[i]; + auto &[transform, transformOrigin] = transforms[i]; if (transform.operations.empty()) { continue; } else if (i > 0) { parentHasTransform = true; } if (i == 0 && !parentHasTransform) { - // If only target view has transform, lets skip it, to matrix decomposition in JS + // If only target view has transform, lets skip it, to matrix + // decomposition in JS break; } - transformOrigin.xy[0].value -= targetViewPosition.x - absolutePositions[i].x; - transformOrigin.xy[1].value -= targetViewPosition.y - absolutePositions[i].y; - combinedMatrix = combinedMatrix * resolveTransform(node->current.layoutMetrics, transform, transformOrigin); + transformOrigin.xy[0].value -= + targetViewPosition.x - absolutePositions[i].x; + transformOrigin.xy[1].value -= + targetViewPosition.y - absolutePositions[i].y; + combinedMatrix = + combinedMatrix * + resolveTransform( + node->current.layoutMetrics, transform, transformOrigin); combinedMatrix.operations.clear(); } if (parentHasTransform) { transformForNode_[node->current.tag] = Transform::FromTransformOperation( - react::TransformOperation(TransformOperationType::Arbitrary), - {}, - combinedMatrix - ); + react::TransformOperation(TransformOperationType::Arbitrary), + {}, + combinedMatrix); } } -// The methods resolveTransform and getTranslateForTransformOrigin are sourced from: +// The methods resolveTransform and getTranslateForTransformOrigin are sourced +// from: // https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.cpp#L548 // We need a copy of these methods to modify the `resolveTransform` method // to accept the transform origin as a parameter instead of as a class field. react::Transform LayoutAnimationsProxy::resolveTransform( - const LayoutMetrics &layoutMetrics, - const Transform &transform, - const TransformOrigin &transformOrigin -) const { - const auto& frameSize = layoutMetrics.frame.size; + const LayoutMetrics &layoutMetrics, + const Transform &transform, + const TransformOrigin &transformOrigin) const { + const auto &frameSize = layoutMetrics.frame.size; auto transformMatrix = Transform{}; if (frameSize.width == 0 && frameSize.height == 0) { return transformMatrix; } if (transform.operations.size() == 1 && - transform.operations[0].type == facebook::react::TransformOperationType::Arbitrary) { + transform.operations[0].type == + facebook::react::TransformOperationType::Arbitrary) { transformMatrix = transform; } else { - for (const auto& operation : transform.operations) { - transformMatrix = - transformMatrix - * Transform::FromTransformOperation(operation, layoutMetrics.frame.size, transform); + for (const auto &operation : transform.operations) { + transformMatrix = transformMatrix * + Transform::FromTransformOperation( + operation, layoutMetrics.frame.size, transform); } } if (transformOrigin.isSet()) { std::array translateOffsets = getTranslateForTransformOrigin( - frameSize.width, frameSize.height, transformOrigin); + frameSize.width, frameSize.height, transformOrigin); transformMatrix = - Transform::Translate(translateOffsets[0], translateOffsets[1], translateOffsets[2]) - * transformMatrix - * Transform::Translate(-translateOffsets[0], -translateOffsets[1], -translateOffsets[2]); + Transform::Translate( + translateOffsets[0], translateOffsets[1], translateOffsets[2]) * + transformMatrix * + Transform::Translate( + -translateOffsets[0], -translateOffsets[1], -translateOffsets[2]); } return transformMatrix; } std::array LayoutAnimationsProxy::getTranslateForTransformOrigin( - float viewWidth, - float viewHeight, - const TransformOrigin &transformOrigin -) const { + float viewWidth, + float viewHeight, + const TransformOrigin &transformOrigin) const { float viewCenterX = viewWidth / 2; float viewCenterY = viewHeight / 2; - std::array origin = { viewCenterX, viewCenterY, transformOrigin.z }; + std::array origin = {viewCenterX, viewCenterY, transformOrigin.z}; for (size_t i = 0; i < transformOrigin.xy.size(); ++i) { - const auto& currentOrigin = transformOrigin.xy[i]; + const auto ¤tOrigin = transformOrigin.xy[i]; if (currentOrigin.unit == UnitType::Point) { origin[i] = currentOrigin.value; } else if (currentOrigin.unit == UnitType::Percent) { - origin[i] = ((i == 0) ? viewWidth : viewHeight) * currentOrigin.value / 100.0f; + origin[i] = + ((i == 0) ? viewWidth : viewHeight) * currentOrigin.value / 100.0f; } } @@ -329,53 +381,71 @@ std::array LayoutAnimationsProxy::getTranslateForTransformOrigin( float newTranslateY = -viewCenterY + origin[1]; float newTranslateZ = origin[2]; - return { newTranslateX, newTranslateY, newTranslateZ }; + return {newTranslateX, newTranslateY, newTranslateZ}; } -void LayoutAnimationsProxy::handleProgressTransition(ShadowViewMutationList &filteredMutations, const ShadowViewMutationList &mutations, const PropsParserContext &propsParserContext, SurfaceId surfaceId) const { +void LayoutAnimationsProxy::handleProgressTransition( + ShadowViewMutationList &filteredMutations, + const ShadowViewMutationList &mutations, + const PropsParserContext &propsParserContext, + SurfaceId surfaceId) const { LOG(INFO) << "Transition state: " << transitionState_; - if (!transitionUpdated_){ + if (!transitionUpdated_) { return; } transitionUpdated_ = false; - - if (mutations.size() == 0 && transitionState_){ - if (transitionState_ == START){ + + if (mutations.size() == 0 && transitionState_) { + if (transitionState_ == START) { auto root = lightNodes_[surfaceId]; auto beforeTopScreen = topScreen[surfaceId]; auto afterTopScreen = lightNodes_[transitionTag_]; - if (beforeTopScreen && afterTopScreen){ - LOG(INFO) << "start progress transition: " << beforeTopScreen->current.tag << " -> " << afterTopScreen->current.tag; - + if (beforeTopScreen && afterTopScreen) { + LOG(INFO) << "start progress transition: " + << beforeTopScreen->current.tag << " -> " + << afterTopScreen->current.tag; + findSharedElementsOnScreen(beforeTopScreen, 0); findSharedElementsOnScreen(afterTopScreen, 1); - - if (beforeTopScreen->current.tag != afterTopScreen->current.tag){ - - for (auto& [sharedTag, transition]: transitions_){ - const auto& [before, after] = transition.snapshot; - const auto& [beforeParentTag, afterParentTag] = transition.parentTag; - - auto& root = lightNodes_[surfaceId]; + + if (beforeTopScreen->current.tag != afterTopScreen->current.tag) { + for (auto &[sharedTag, transition] : transitions_) { + const auto &[before, after] = transition.snapshot; + const auto &[beforeParentTag, afterParentTag] = + transition.parentTag; + + auto &root = lightNodes_[surfaceId]; ShadowView s = before; s.tag = myTag; filteredMutations.push_back(ShadowViewMutation::CreateMutation(s)); - filteredMutations.push_back(ShadowViewMutation::InsertMutation(surfaceId, s, root->children.size())); - filteredMutations.push_back(ShadowViewMutation::UpdateMutation(after, after, afterParentTag)); + filteredMutations.push_back(ShadowViewMutation::InsertMutation( + surfaceId, s, root->children.size())); + filteredMutations.push_back(ShadowViewMutation::UpdateMutation( + after, after, afterParentTag)); auto p = lightNodes_[before.tag]->parent.lock(); - auto m1 = ShadowViewMutation::InsertMutation(p->current.tag, before, 8); - filteredMutations.push_back(ShadowViewMutation::UpdateMutation(before, *cloneViewWithoutOpacity(m1, propsParserContext), p->current.tag)); - - - auto m = ShadowViewMutation::UpdateMutation(after, after, afterParentTag); - m = ShadowViewMutation::UpdateMutation(after, *cloneViewWithoutOpacity(m, propsParserContext), afterParentTag); + auto m1 = + ShadowViewMutation::InsertMutation(p->current.tag, before, 8); + filteredMutations.push_back(ShadowViewMutation::UpdateMutation( + before, + *cloneViewWithoutOpacity(m1, propsParserContext), + p->current.tag)); + + auto m = ShadowViewMutation::UpdateMutation( + after, after, afterParentTag); + m = ShadowViewMutation::UpdateMutation( + after, + *cloneViewWithoutOpacity(m, propsParserContext), + afterParentTag); filteredMutations.push_back(m); auto node = std::make_shared(); node->current = s; lightNodes_[myTag] = node; - + root->children.push_back(node); - layoutAnimationsManager_->getConfigsForType(LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[myTag] = layoutAnimationsManager_->getConfigsForType(LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[before.tag]; + layoutAnimationsManager_->getConfigsForType( + LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[myTag] = + layoutAnimationsManager_->getConfigsForType( + LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[before.tag]; ShadowView copy = after; copy.tag = myTag; auto copy2 = before; @@ -385,36 +455,47 @@ void LayoutAnimationsProxy::handleProgressTransition(ShadowViewMutationList &fil restoreMap_[myTag][1] = after.tag; sharedTransitionManager_->groups_[sharedTag].fakeTag = myTag; activeTransitions_.insert(myTag); - myTag+=2; - + myTag += 2; } } } } else if (transitionState_ == ACTIVE) { - for (auto tag: activeTransitions_){ + for (auto tag : activeTransitions_) { auto layoutAnimation = layoutAnimations_[tag]; auto &updateMap = - surfaceManager.getUpdateMap(layoutAnimation.finalView->surfaceId); + surfaceManager.getUpdateMap(layoutAnimation.finalView->surfaceId); auto before = layoutAnimation.startView->layoutMetrics.frame; auto after = layoutAnimation.finalView->layoutMetrics.frame; - auto x = before.origin.x + transitionProgress_*(after.origin.x - before.origin.x); - auto y = before.origin.y + transitionProgress_*(after.origin.y - before.origin.y); - auto width = before.size.width + transitionProgress_*(after.size.width - before.size.width); - auto height = before.size.height + transitionProgress_*(after.size.height - before.size.height); - - auto beforeProps = std::static_pointer_cast(layoutAnimation.startView->props); - auto afterProps = std::static_pointer_cast(layoutAnimation.finalView->props); - auto beforeRadius = beforeProps->borderRadii.all.value_or(ValueUnit(0, UnitType::Point)).value; - auto afterRadius = afterProps->borderRadii.all.value_or(ValueUnit(0, UnitType::Point)).value; - - auto d = folly::dynamic::object("borderRadius", beforeRadius + transitionProgress_*(afterRadius - beforeRadius)); - - #ifdef RN_SERIALIZABLE_STATE -// auto rawProps = RawProps(folly::dynamic::merge( -// layoutAnimation.finalView->props->rawProps, d)); + auto x = before.origin.x + + transitionProgress_ * (after.origin.x - before.origin.x); + auto y = before.origin.y + + transitionProgress_ * (after.origin.y - before.origin.y); + auto width = before.size.width + + transitionProgress_ * (after.size.width - before.size.width); + auto height = before.size.height + + transitionProgress_ * (after.size.height - before.size.height); + + auto beforeProps = std::static_pointer_cast( + layoutAnimation.startView->props); + auto afterProps = std::static_pointer_cast( + layoutAnimation.finalView->props); + auto beforeRadius = + beforeProps->borderRadii.all.value_or(ValueUnit(0, UnitType::Point)) + .value; + auto afterRadius = + afterProps->borderRadii.all.value_or(ValueUnit(0, UnitType::Point)) + .value; + + auto d = folly::dynamic::object( + "borderRadius", + beforeRadius + transitionProgress_ * (afterRadius - beforeRadius)); + +#ifdef RN_SERIALIZABLE_STATE + // auto rawProps = RawProps(folly::dynamic::merge( + // layoutAnimation.finalView->props->rawProps, d)); Props::Shared newProps = nullptr; - #else - auto rawProps = RawProps(std::move(d)); +#else + auto rawProps = RawProps(std::move(d)); auto newProps = getComponentDescriptorForShadowView(*layoutAnimation.finalView) @@ -422,24 +503,24 @@ void LayoutAnimationsProxy::handleProgressTransition(ShadowViewMutationList &fil propsParserContext, layoutAnimation.finalView->props, std::move(rawProps)); - #endif - - updateMap.insert_or_assign(tag, UpdateValues{newProps, {x,y,width,height}}); +#endif + + updateMap.insert_or_assign( + tag, UpdateValues{newProps, {x, y, width, height}}); } } - - - if (transitionState_ == START){ + + if (transitionState_ == START) { transitionState_ = ACTIVE; - } else if (transitionState_ == END || transitionState_ == CANCELLED){ - for (auto tag: activeTransitions_){ + } else if (transitionState_ == END || transitionState_ == CANCELLED) { + for (auto tag : activeTransitions_) { sharedContainersToRemove_.push_back(tag); tagsToRestore_.push_back(restoreMap_[tag][1]); - if (transitionState_ == CANCELLED){ + if (transitionState_ == CANCELLED) { tagsToRestore_.push_back(restoreMap_[tag][0]); } } - if (transitionState_ == END){ + if (transitionState_ == END) { topScreen[surfaceId] = lightNodes_[transitionTag_]; synchronized_ = false; } @@ -450,12 +531,14 @@ void LayoutAnimationsProxy::handleProgressTransition(ShadowViewMutationList &fil } } -void LayoutAnimationsProxy::updateLightTree(const PropsParserContext& propsParserContext, const ShadowViewMutationList &mutations, ShadowViewMutationList& filteredMutations) const { - +void LayoutAnimationsProxy::updateLightTree( + const PropsParserContext &propsParserContext, + const ShadowViewMutationList &mutations, + ShadowViewMutationList &filteredMutations) const { std::unordered_set moved, deleted; - for (auto it = mutations.rbegin(); it != mutations.rend(); it++){ - const auto& mutation = *it; - switch (mutation.type){ + for (auto it = mutations.rbegin(); it != mutations.rend(); it++) { + const auto &mutation = *it; + switch (mutation.type) { case ShadowViewMutation::Delete: { deleted.insert(mutation.oldChildShadowView.tag); break; @@ -466,77 +549,89 @@ void LayoutAnimationsProxy::updateLightTree(const PropsParserContext& propsParse } case ShadowViewMutation::Remove: { const auto tag = mutation.oldChildShadowView.tag; - if (deleted.contains(tag)){ + if (deleted.contains(tag)) { lightNodes_[tag]->intent = TO_DELETE; - } else if (moved.contains(tag)){ - lightNodes_[tag]-> intent = TO_MOVE; + } else if (moved.contains(tag)) { + lightNodes_[tag]->intent = TO_MOVE; } break; } - default:{} + default: { + } } } - - for (auto &mutation: mutations){ + + for (auto &mutation : mutations) { maybeUpdateWindowDimensions(mutation); switch (mutation.type) { - case ShadowViewMutation::Update:{ - auto& node = lightNodes_[mutation.newChildShadowView.tag]; - if (!node){ + case ShadowViewMutation::Update: { + auto &node = lightNodes_[mutation.newChildShadowView.tag]; + if (!node) { node = std::make_shared(); } node->previous = mutation.oldChildShadowView; - #ifdef ANDROID +#ifdef ANDROID if (node->current.props) { // on android rawProps are used to store the diffed props // so we need to merge them - // this should soon be replaced in RN with Props 2.0 (the diffing will be done at the end of the pipeline) - auto& currentRawProps = node->current.props->rawProps; - auto mergedRawProps = folly::dynamic::merge(currentRawProps, mutation.newChildShadowView.props->rawProps); - node->current = mutation.newChildShadowView; - node->current.props = getComponentDescriptorForShadowView(node->current).cloneProps(propsParserContext, mutation.newChildShadowView.props, RawProps(mergedRawProps)); + // this should soon be replaced in RN with Props 2.0 (the diffing will + // be done at the end of the pipeline) + auto ¤tRawProps = node->current.props->rawProps; + auto mergedRawProps = folly::dynamic::merge( + currentRawProps, mutation.newChildShadowView.props->rawProps); + node->current = mutation.newChildShadowView; + node->current.props = + getComponentDescriptorForShadowView(node->current) + .cloneProps( + propsParserContext, + mutation.newChildShadowView.props, + RawProps(mergedRawProps)); } else { - node->current = mutation.newChildShadowView; + node->current = mutation.newChildShadowView; } - #else +#else node->current = mutation.newChildShadowView; - #endif +#endif auto tag = mutation.newChildShadowView.tag; - if (layoutAnimationsManager_->hasLayoutAnimation(tag, LAYOUT)){ + if (layoutAnimationsManager_->hasLayoutAnimation(tag, LAYOUT)) { layout_.push_back(node); } else { filteredMutations.push_back(mutation); } break; } - case ShadowViewMutation::Create:{ - auto& node = lightNodes_[mutation.newChildShadowView.tag]; + case ShadowViewMutation::Create: { + auto &node = lightNodes_[mutation.newChildShadowView.tag]; node = std::make_shared(); node->current = mutation.newChildShadowView; filteredMutations.push_back(mutation); break; } - case ShadowViewMutation::Delete:{ + case ShadowViewMutation::Delete: { // lightNodes_.erase(mutation.oldChildShadowView.tag); break; } - case ShadowViewMutation::Insert:{ + case ShadowViewMutation::Insert: { transferConfigFromNativeID( - mutation.newChildShadowView.props->nativeId, - mutation.newChildShadowView.tag); - auto& node = lightNodes_[mutation.newChildShadowView.tag]; - auto& parent = lightNodes_[mutation.parentTag]; - parent->children.insert(parent->children.begin()+mutation.index, node); + mutation.newChildShadowView.props->nativeId, + mutation.newChildShadowView.tag); + auto &node = lightNodes_[mutation.newChildShadowView.tag]; + auto &parent = lightNodes_[mutation.parentTag]; + parent->children.insert( + parent->children.begin() + mutation.index, node); node->parent = parent; const auto tag = mutation.newChildShadowView.tag; - if (node->intent == TO_MOVE && layoutAnimationsManager_->hasLayoutAnimation(tag, LAYOUT)){ + if (node->intent == TO_MOVE && + layoutAnimationsManager_->hasLayoutAnimation(tag, LAYOUT)) { // TODO: figure out if that's true - // we are not starting the animation here because any update will come from the UPDATE mutation -// layout_.push_back(node); + // we are not starting the animation here because any update will come + // from the UPDATE mutation + // layout_.push_back(node); filteredMutations.push_back(mutation); -// node->previous = node->current; -// node->current = mutation.newChildShadowView; - } else if (layoutAnimationsManager_->hasLayoutAnimation(tag, ENTERING)){ + // node->previous = node->current; + // node->current = mutation.newChildShadowView; + } else if (layoutAnimationsManager_->hasLayoutAnimation( + tag, ENTERING)) { entering_.push_back(node); filteredMutations.push_back(mutation); } else { @@ -544,27 +639,29 @@ void LayoutAnimationsProxy::updateLightTree(const PropsParserContext& propsParse } break; } - case ShadowViewMutation::Remove:{ - auto& node = lightNodes_[mutation.oldChildShadowView.tag]; - auto& parent = lightNodes_[mutation.parentTag]; - - if (node->intent == TO_DELETE && parent->intent != TO_DELETE){ + case ShadowViewMutation::Remove: { + auto &node = lightNodes_[mutation.oldChildShadowView.tag]; + auto &parent = lightNodes_[mutation.parentTag]; + + if (node->intent == TO_DELETE && parent->intent != TO_DELETE) { exiting_.push_back(node); LOG(INFO) << "remove3 " << node->current.tag; - if (parent->children[mutation.index]->current.tag == mutation.oldChildShadowView.tag){ + if (parent->children[mutation.index]->current.tag == + mutation.oldChildShadowView.tag) { filteredMutations.push_back(mutation); } else { throw "cos jest nie tak z indexami"; } - parent->children.erase(parent->children.begin()+mutation.index); - } else if (node->intent != TO_DELETE){ + parent->children.erase(parent->children.begin() + mutation.index); + } else if (node->intent != TO_DELETE) { LOG(INFO) << "remove4 " << node->current.tag; - if (parent->children[mutation.index]->current.tag == mutation.oldChildShadowView.tag){ + if (parent->children[mutation.index]->current.tag == + mutation.oldChildShadowView.tag) { filteredMutations.push_back(mutation); } else { throw "cos jest nie tak z indexami"; } - parent->children.erase(parent->children.begin()+mutation.index); + parent->children.erase(parent->children.begin() + mutation.index); } break; } @@ -575,69 +672,99 @@ void LayoutAnimationsProxy::updateLightTree(const PropsParserContext& propsParse } void printTree(LightNode::Unshared &node, int level) { - if (!(strcmp(node->current.componentName, "RNSScreen"))) { + if (!(strcmp(node->current.componentName, "RNSScreen"))) { // bool isActive = false; #ifdef ANDROID - float f = node->current.props->rawProps.getDefault("activityState", 0).asDouble(); + float f = + node->current.props->rawProps.getDefault("activityState", 0).asDouble(); // isActive = f == 2.0f; #else - float f = std::static_pointer_cast(node->current.props)->activityState; + float f = + std::static_pointer_cast(node->current.props) + ->activityState; #endif - LOG(INFO) << "screen start (activityState: " << f << ") " << node->current.tag << " " << level; - } else { -// LOG(INFO) << node->current.componentName << " " << node->current.tag << " " << level; - } - for (auto& child: node->children){ - printTree(child, level + 1); - } - if (!(strcmp(node->current.componentName, "RNSScreen"))) { - LOG(INFO) << "screen end" << " " << node->current.tag; - } + LOG(INFO) << "screen start (activityState: " << f << ") " + << node->current.tag << " " << level; + } else { + // LOG(INFO) << node->current.componentName << " " << + // node->current.tag << " " << level; + } + for (auto &child : node->children) { + printTree(child, level + 1); + } + if (!(strcmp(node->current.componentName, "RNSScreen"))) { + LOG(INFO) << "screen end" << " " << node->current.tag; + } } -void LayoutAnimationsProxy::handleSharedTransitionsStart(const LightNode::Unshared &afterTopScreen, const LightNode::Unshared &beforeTopScreen, ShadowViewMutationList &filteredMutations, const ShadowViewMutationList &mutations, const PropsParserContext &propsParserContext, SurfaceId surfaceId) const { +void LayoutAnimationsProxy::handleSharedTransitionsStart( + const LightNode::Unshared &afterTopScreen, + const LightNode::Unshared &beforeTopScreen, + ShadowViewMutationList &filteredMutations, + const ShadowViewMutationList &mutations, + const PropsParserContext &propsParserContext, + SurfaceId surfaceId) const { { ReanimatedSystraceSection s1("moj narzut 2"); - - if (beforeTopScreen && afterTopScreen && beforeTopScreen->current.tag != afterTopScreen->current.tag){ + + if (beforeTopScreen && afterTopScreen && + beforeTopScreen->current.tag != afterTopScreen->current.tag) { LOG(INFO) << "different tags"; - LOG(INFO) << "start transition: " << beforeTopScreen->current.tag << " -> " << afterTopScreen->current.tag; - - for (auto& [sharedTag, transition]: transitions_){ + LOG(INFO) << "start transition: " << beforeTopScreen->current.tag + << " -> " << afterTopScreen->current.tag; + + for (auto &[sharedTag, transition] : transitions_) { LOG(INFO) << "sharedTag: " << sharedTag; - const auto& [before, after] = transition.snapshot; - const auto& [beforeParentTag, afterParentTag] = transition.parentTag; - + const auto &[before, after] = transition.snapshot; + const auto &[beforeParentTag, afterParentTag] = transition.parentTag; + auto fakeTag = sharedTransitionManager_->groups_[sharedTag].fakeTag; - auto shouldCreateContainer = (fakeTag == -1 || !layoutAnimations_.contains(fakeTag)); - if (shouldCreateContainer){ - auto& root = lightNodes_[surfaceId]; + auto shouldCreateContainer = + (fakeTag == -1 || !layoutAnimations_.contains(fakeTag)); + if (shouldCreateContainer) { + auto &root = lightNodes_[surfaceId]; ShadowView s = before; - auto beforeViewProps = std::const_pointer_cast(std::static_pointer_cast(s.props)); - auto afterViewProps = std::const_pointer_cast(std::static_pointer_cast(after.props)); + auto beforeViewProps = std::const_pointer_cast( + std::static_pointer_cast(s.props)); + auto afterViewProps = std::const_pointer_cast( + std::static_pointer_cast(after.props)); s.tag = myTag; - if (transformForNode_.contains(before.tag)){ + if (transformForNode_.contains(before.tag)) { #ifdef ANDROID - auto array = folly::dynamic::array(folly::dynamic::object("matrix", transformForNode_[before.tag].operator folly::dynamic())); - folly::dynamic newTransformDynamic = folly::dynamic::object("transform", array); - auto newRawProps = folly::dynamic::merge(s.props->rawProps, newTransformDynamic); - auto newProps = getComponentDescriptorForShadowView(s).cloneProps(propsParserContext, s.props, RawProps(newRawProps)); - auto viewProps = std::const_pointer_cast(std::static_pointer_cast(newProps)); + auto array = folly::dynamic::array(folly::dynamic::object( + "matrix", + transformForNode_[before.tag].operator folly::dynamic())); + folly::dynamic newTransformDynamic = + folly::dynamic::object("transform", array); + auto newRawProps = + folly::dynamic::merge(s.props->rawProps, newTransformDynamic); + auto newProps = getComponentDescriptorForShadowView(s).cloneProps( + propsParserContext, s.props, RawProps(newRawProps)); + auto viewProps = std::const_pointer_cast( + std::static_pointer_cast(newProps)); #else - auto newProps = getComponentDescriptorForShadowView(s).cloneProps(propsParserContext, s.props, {}); - auto viewProps = std::const_pointer_cast(std::static_pointer_cast(newProps)); + auto newProps = getComponentDescriptorForShadowView(s).cloneProps( + propsParserContext, s.props, {}); + auto viewProps = std::const_pointer_cast( + std::static_pointer_cast(newProps)); viewProps->transform = transformForNode_[before.tag]; #endif s.props = newProps; } filteredMutations.push_back(ShadowViewMutation::CreateMutation(s)); - filteredMutations.push_back(ShadowViewMutation::InsertMutation(surfaceId, s, root->children.size())); - filteredMutations.push_back(ShadowViewMutation::UpdateMutation(after, after, afterParentTag)); - auto m = ShadowViewMutation::UpdateMutation(after, after, afterParentTag); - m = ShadowViewMutation::UpdateMutation(after, *cloneViewWithoutOpacity(m, propsParserContext), afterParentTag); + filteredMutations.push_back(ShadowViewMutation::InsertMutation( + surfaceId, s, root->children.size())); + filteredMutations.push_back( + ShadowViewMutation::UpdateMutation(after, after, afterParentTag)); + auto m = + ShadowViewMutation::UpdateMutation(after, after, afterParentTag); + m = ShadowViewMutation::UpdateMutation( + after, + *cloneViewWithoutOpacity(m, propsParserContext), + afterParentTag); filteredMutations.push_back(m); auto node = std::make_shared(); node->current = s; @@ -645,83 +772,102 @@ void LayoutAnimationsProxy::handleSharedTransitionsStart(const LightNode::Unshar root->children.push_back(node); fakeTag = myTag; } - layoutAnimationsManager_->getConfigsForType(LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[fakeTag] = layoutAnimationsManager_->getConfigsForType(LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[before.tag]; + layoutAnimationsManager_->getConfigsForType( + LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[fakeTag] = + layoutAnimationsManager_->getConfigsForType( + LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[before.tag]; ShadowView copy = after; copy.tag = fakeTag; auto copy2 = before; copy2.tag = fakeTag; - startSharedTransition(fakeTag, copy2, copy, surfaceId, before.tag, after.tag); + startSharedTransition( + fakeTag, copy2, copy, surfaceId, before.tag, after.tag); restoreMap_[fakeTag][1] = after.tag; - if (shouldCreateContainer){ + if (shouldCreateContainer) { sharedTransitionManager_->groups_[sharedTag].fakeTag = myTag; - myTag+=2; + myTag += 2; } } - } else if (mutations.size() && beforeTopScreen && afterTopScreen && beforeTopScreen->current.tag == afterTopScreen->current.tag){ + } else if ( + mutations.size() && beforeTopScreen && afterTopScreen && + beforeTopScreen->current.tag == afterTopScreen->current.tag) { LOG(INFO) << "same tag"; - for (auto& [sharedTag, transition]: transitions_){ - const auto& [_, after] = transition.snapshot; - + for (auto &[sharedTag, transition] : transitions_) { + const auto &[_, after] = transition.snapshot; + auto copy = after; auto fakeTag = sharedTransitionManager_->groups_[sharedTag].fakeTag; copy.tag = fakeTag; - if (!layoutAnimations_.contains(fakeTag)){ + if (!layoutAnimations_.contains(fakeTag)) { continue; } - auto& la = layoutAnimations_[fakeTag]; - if (la.finalView->layoutMetrics != copy.layoutMetrics){ - startSharedTransition(fakeTag, copy, copy, surfaceId, la.finalView->tag, after.tag); + auto &la = layoutAnimations_[fakeTag]; + if (la.finalView->layoutMetrics != copy.layoutMetrics) { + startSharedTransition( + fakeTag, copy, copy, surfaceId, la.finalView->tag, after.tag); } } } } } -void LayoutAnimationsProxy::cleanupSharedTransitions(ShadowViewMutationList &filteredMutations, const PropsParserContext &propsParserContext, SurfaceId surfaceId) const { - for (auto& tag: tagsToRestore_){ - auto& node = lightNodes_[tag]; - if (node){ +void LayoutAnimationsProxy::cleanupSharedTransitions( + ShadowViewMutationList &filteredMutations, + const PropsParserContext &propsParserContext, + SurfaceId surfaceId) const { + for (auto &tag : tagsToRestore_) { + auto &node = lightNodes_[tag]; + if (node) { auto view = node->current; auto parentTag = node->parent.lock()->current.tag; auto m = ShadowViewMutation::UpdateMutation(view, view, parentTag); - m = ShadowViewMutation::UpdateMutation(*cloneViewWithoutOpacity(m, propsParserContext), *cloneViewWithOpacity(m, propsParserContext), parentTag); + m = ShadowViewMutation::UpdateMutation( + *cloneViewWithoutOpacity(m, propsParserContext), + *cloneViewWithOpacity(m, propsParserContext), + parentTag); filteredMutations.push_back(m); } } tagsToRestore_.clear(); - - for (auto& tag: sharedContainersToRemove_){ + + for (auto &tag : sharedContainersToRemove_) { auto root = lightNodes_[surfaceId]; - for (int i=0; i< root->children.size(); i++){ - auto& child = root->children[i]; - if (child->current.tag == tag){ - filteredMutations.push_back(ShadowViewMutation::RemoveMutation(surfaceId, child->current, i)); - filteredMutations.push_back(ShadowViewMutation::DeleteMutation(child->current)); + for (int i = 0; i < root->children.size(); i++) { + auto &child = root->children[i]; + if (child->current.tag == tag) { + filteredMutations.push_back( + ShadowViewMutation::RemoveMutation(surfaceId, child->current, i)); + filteredMutations.push_back( + ShadowViewMutation::DeleteMutation(child->current)); LOG(INFO) << "delete container " << tag; - root->children.erase(root->children.begin()+i); + root->children.erase(root->children.begin() + i); } } } sharedContainersToRemove_.clear(); } -void LayoutAnimationsProxy::hideTransitioningViews(int index, ShadowViewMutationList &filteredMutations, const PropsParserContext &propsParserContext) const { - for (auto& [sharedTag, transition]: transitions_){ - const auto& shadowView = transition.snapshot[index]; - const auto& parentTag = transition.parentTag[index]; - auto m = ShadowViewMutation::UpdateMutation(shadowView, shadowView, parentTag); - m = ShadowViewMutation::UpdateMutation(shadowView, *cloneViewWithoutOpacity(m, propsParserContext), parentTag); +void LayoutAnimationsProxy::hideTransitioningViews( + int index, + ShadowViewMutationList &filteredMutations, + const PropsParserContext &propsParserContext) const { + for (auto &[sharedTag, transition] : transitions_) { + const auto &shadowView = transition.snapshot[index]; + const auto &parentTag = transition.parentTag[index]; + auto m = + ShadowViewMutation::UpdateMutation(shadowView, shadowView, parentTag); + m = ShadowViewMutation::UpdateMutation( + shadowView, *cloneViewWithoutOpacity(m, propsParserContext), parentTag); filteredMutations.push_back(m); } } - std::optional LayoutAnimationsProxy::progressLayoutAnimation( int tag, const jsi::Object &newStyle) { -//#ifdef LAYOUT_ANIMATIONS_LOGS + // #ifdef LAYOUT_ANIMATIONS_LOGS LOG(INFO) << "progress layout animation for tag " << tag << std::endl; -//#endif + // #endif auto lock = std::unique_lock(mutex); auto layoutAnimationIt = layoutAnimations_.find(tag); @@ -785,16 +931,15 @@ std::optional LayoutAnimationsProxy::endLayoutAnimation( auto &updateMap = surfaceManager.getUpdateMap(surfaceId); layoutAnimations_.erase(tag); updateMap.erase(tag); - - if (tag >= 10000){ + + if (tag >= 10000) { // TODO fix auto sharedTag = sharedTransitionManager_->tagToName_[tag]; sharedTransitionManager_->groups_.erase(sharedTag); - + sharedContainersToRemove_.push_back(tag); tagsToRestore_.push_back(restoreMap_[tag][1]); transformForNode_.clear(); - } if (!shouldRemove || !lightNodes_.contains(tag)) { return surfaceId; @@ -807,26 +952,34 @@ std::optional LayoutAnimationsProxy::endLayoutAnimation( return surfaceId; } -std::optional LayoutAnimationsProxy::onTransitionProgress(int tag, double progress, bool isClosing, bool isGoingForward, bool isSwiping){ +std::optional LayoutAnimationsProxy::onTransitionProgress( + int tag, + double progress, + bool isClosing, + bool isGoingForward, + bool isSwiping) { auto lock = std::unique_lock(mutex); transitionUpdated_ = true; -// LOG(INFO) << "notifyTransitionProgress ("<< tag <<"): " << progress << ", closing: " << isClosing << ", goingForward: " << isGoingForward << ", isSwiping: " < LayoutAnimationsProxy::onGestureCancel(){ +std::optional LayoutAnimationsProxy::onGestureCancel() { auto lock = std::unique_lock(mutex); - if (transitionState_){ + if (transitionState_) { transitionState_ = CANCELLED; transitionUpdated_ = true; // TODO: unfix @@ -854,42 +1007,45 @@ void LayoutAnimationsProxy::handleRemovals( // 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)) { + + if (startAnimationsRecursively( + node, true, true, false, filteredMutations)) { auto parent = node->parent.lock(); // TODO: handle this better auto current = node->current; - if (layoutAnimations_.contains(node->current.tag)){ + if (layoutAnimations_.contains(node->current.tag)) { current = *layoutAnimations_.at(node->current.tag).currentView; } - filteredMutations.push_back(ShadowViewMutation::InsertMutation(parent->current.tag, current, parent->children.size())); + filteredMutations.push_back(ShadowViewMutation::InsertMutation( + parent->current.tag, current, parent->children.size())); parent->children.push_back(node); parent->animatedChildrenCount++; - if (node->state == UNDEFINED){ + if (node->state == UNDEFINED) { node->state = WAITING; } } else { - maybeCancelAnimation(node->current.tag); - filteredMutations.push_back(ShadowViewMutation::DeleteMutation(node->current)); + maybeCancelAnimation(node->current.tag); + filteredMutations.push_back( + ShadowViewMutation::DeleteMutation(node->current)); #ifdef LAYOUT_ANIMATIONS_LOGS - LOG(INFO) << "delete " << node->tag << std::endl; + LOG(INFO) << "delete " << node->tag << std::endl; #endif } } - for (auto node : deadNodes) { if (node->state != DELETED) { auto parent = node->parent.lock(); int index = 0; - for (auto it = parent->children.begin(); it != parent->children.end(); it++, index++){ + for (auto it = parent->children.begin(); it != parent->children.end(); + it++, index++) { auto n = *it; - if (n->current.tag == node->current.tag){ + if (n->current.tag == node->current.tag) { parent->animatedChildrenCount--; break; } } - + endAnimationsRecursively(node, index, filteredMutations); maybeDropAncestors(node->parent.lock(), node, filteredMutations); } @@ -911,11 +1067,11 @@ void LayoutAnimationsProxy::addOngoingAnimations( auto &layoutAnimation = layoutAnimationIt->second; auto newView = std::make_shared(*layoutAnimation.finalView); - if (updateValues.newProps){ + if (updateValues.newProps) { newView->props = updateValues.newProps; } updateLayoutMetrics(newView->layoutMetrics, updateValues.frame); - + LOG(INFO) << "(addOngoing) " << tag; mutations.push_back(ShadowViewMutation::UpdateMutation( @@ -933,65 +1089,63 @@ void LayoutAnimationsProxy::endAnimationsRecursively( node->state = DELETED; // iterate from the end, so that children // with higher indices appear first in the mutations list - - int i = node->children.size()-1; - for (auto it = node->children.rbegin(); - it != node->children.rend(); - it++) { + + int i = node->children.size() - 1; + for (auto it = node->children.rbegin(); it != node->children.rend(); it++) { auto &subNode = *it; if (subNode->state != DELETED) { endAnimationsRecursively(subNode, i--, mutations); } } node->children.clear(); - LOG(INFO) << "remove1 " << node->current.tag; - mutations.push_back(ShadowViewMutation::RemoveMutation(node->parent.lock()->current.tag, node->current, index)); + LOG(INFO) << "remove1 " << node->current.tag; + mutations.push_back(ShadowViewMutation::RemoveMutation( + node->parent.lock()->current.tag, node->current, index)); // nodeForTag_.erase(node->tag); #ifdef LAYOUT_ANIMATIONS_LOGS LOG(INFO) << "delete " << node->tag << std::endl; #endif - mutations.push_back( - ShadowViewMutation::DeleteMutation(node->current)); + mutations.push_back(ShadowViewMutation::DeleteMutation(node->current)); } void LayoutAnimationsProxy::maybeDropAncestors( std::shared_ptr parent, std::shared_ptr child, ShadowViewMutationList &cleanupMutations) const { -// parent->removeChildFromUnflattenedTree(child); -// if (!parent->isMutationMode()) { -// return; -// } - for (auto it = parent->children.begin(); it != parent->children.end(); it++){ - if ((*it)->current.tag == child->current.tag){ - parent->children.erase(it); - break; - } - } - if (parent->state == UNDEFINED){ - return; - } + // parent->removeChildFromUnflattenedTree(child); + // if (!parent->isMutationMode()) { + // return; + // } + for (auto it = parent->children.begin(); it != parent->children.end(); it++) { + if ((*it)->current.tag == child->current.tag) { + parent->children.erase(it); + break; + } + } + if (parent->state == UNDEFINED) { + return; + } -// auto node = std::static_pointer_cast(parent); + // auto node = std::static_pointer_cast(parent); if (parent->children.size() == 0 && parent->state != ANIMATING) { -// nodeForTag_.erase(parent->current.tag); + // nodeForTag_.erase(parent->current.tag); auto pp = parent->parent.lock(); - for (int i=0; ichildren.size(); i++){ - if (pp->children[i]->current.tag == parent->current.tag){ - LOG(INFO) << "remove2 " << parent->current.tag << ", "<state; - cleanupMutations.push_back(ShadowViewMutation::RemoveMutation(pp->current.tag, parent->current, i)); + for (int i = 0; i < pp->children.size(); i++) { + if (pp->children[i]->current.tag == parent->current.tag) { + LOG(INFO) << "remove2 " << parent->current.tag << ", " << parent->state; + cleanupMutations.push_back(ShadowViewMutation::RemoveMutation( + pp->current.tag, parent->current, i)); maybeCancelAnimation(parent->current.tag); - #ifdef LAYOUT_ANIMATIONS_LOGS +#ifdef LAYOUT_ANIMATIONS_LOGS LOG(INFO) << "delete " << node->tag << std::endl; - #endif +#endif cleanupMutations.push_back( ShadowViewMutation::DeleteMutation(parent->current)); maybeDropAncestors(parent->parent.lock(), parent, cleanupMutations); break; } } - } } @@ -1012,7 +1166,8 @@ bool LayoutAnimationsProxy::startAnimationsRecursively( } shouldAnimate = !isScreenPop && - layoutAnimationsManager_->shouldAnimateExiting(node->current.tag, shouldAnimate); + layoutAnimationsManager_->shouldAnimateExiting( + node->current.tag, shouldAnimate); bool hasExitAnimation = shouldAnimate && layoutAnimationsManager_->hasLayoutAnimation( @@ -1026,9 +1181,7 @@ bool LayoutAnimationsProxy::startAnimationsRecursively( // iterate from the end, so that children // with higher indices appear first in the mutations list auto index = node->children.size(); - for (auto it = node->children.rbegin(); - it != node->children.rend(); - it++) { + for (auto it = node->children.rbegin(); it != node->children.rend(); it++) { index--; auto &subNode = *it; #ifdef LAYOUT_ANIMATIONS_LOGS @@ -1056,8 +1209,10 @@ bool LayoutAnimationsProxy::startAnimationsRecursively( hasAnimatedChildren = true; } else if (shouldRemoveSubviewsWithoutAnimations) { maybeCancelAnimation(subNode->current.tag); - LOG(INFO) << "remove " << subNode->current.tag << " from " << node->current.tag << " at " << index; - mutations.push_back(ShadowViewMutation::RemoveMutation(node->current.tag, subNode->current, index)); + LOG(INFO) << "remove " << subNode->current.tag << " from " + << node->current.tag << " at " << index; + mutations.push_back(ShadowViewMutation::RemoveMutation( + node->current.tag, subNode->current, index)); toBeRemoved.push_back(subNode); subNode->state = DELETED; // nodeForTag_.erase(subNode->tag); @@ -1065,8 +1220,7 @@ bool LayoutAnimationsProxy::startAnimationsRecursively( LOG(INFO) << "delete " << subNode->tag << std::endl; #endif mutations.push_back(ShadowViewMutation::DeleteMutation(subNode->current)); - } - else { + } else { subNode->state = WAITING; } } @@ -1079,9 +1233,12 @@ bool LayoutAnimationsProxy::startAnimationsRecursively( if (hasExitAnimation) { node->state = ANIMATING; - startExitingAnimation(node->current.tag, ShadowViewMutation::RemoveMutation(node->parent.lock()->current.tag, node->current, 0)); + startExitingAnimation( + node->current.tag, + ShadowViewMutation::RemoveMutation( + node->parent.lock()->current.tag, node->current, 0)); } else { -// layoutAnimationsManager_->clearLayoutAnimationConfig(node->tag); + // layoutAnimationsManager_->clearLayoutAnimationConfig(node->tag); } return wantAnimateExit; @@ -1110,12 +1267,12 @@ void LayoutAnimationsProxy::createLayoutAnimation( ? mutation.oldChildShadowView : mutation.newChildShadowView); auto currentView = std::make_shared(oldView); - auto startView =std::make_shared(oldView); + auto startView = std::make_shared(oldView); layoutAnimations_.insert_or_assign( tag, - LayoutAnimation{finalView, currentView, startView, mutation.parentTag, {}, count}); - + LayoutAnimation{ + finalView, currentView, startView, mutation.parentTag, {}, count}); } void LayoutAnimationsProxy::startEnteringAnimation( @@ -1149,11 +1306,7 @@ void LayoutAnimationsProxy::startEnteringAnimation( strongThis->layoutAnimations_.insert_or_assign( tag, LayoutAnimation{ - finalView, - current, - nullptr, - mutation.parentTag, - opacity}); + finalView, current, nullptr, mutation.parentTag, opacity}); window = strongThis->surfaceManager.getWindow( mutation.newChildShadowView.surfaceId); } @@ -1267,8 +1420,13 @@ void LayoutAnimationsProxy::startLayoutAnimation( }); } -void LayoutAnimationsProxy::startSharedTransition(const int tag, const ShadowView &before, const ShadowView &after, SurfaceId surfaceId, const int tagBefore, const int tagAfter) const{ - +void LayoutAnimationsProxy::startSharedTransition( + const int tag, + const ShadowView &before, + const ShadowView &after, + SurfaceId surfaceId, + const int tagBefore, + const int tagAfter) const { uiScheduler_->scheduleOnUI([weakThis = weak_from_this(), before, after, @@ -1286,7 +1444,11 @@ void LayoutAnimationsProxy::startSharedTransition(const int tag, const ShadowVie { auto &mutex = strongThis->mutex; auto lock = std::unique_lock(mutex); - strongThis->createLayoutAnimation(ShadowViewMutation::InsertMutation(surfaceId, after, 1), oldView, surfaceId, tag); + strongThis->createLayoutAnimation( + ShadowViewMutation::InsertMutation(surfaceId, after, 1), + oldView, + surfaceId, + tag); window = strongThis->surfaceManager.getWindow(surfaceId); } @@ -1294,43 +1456,55 @@ void LayoutAnimationsProxy::startSharedTransition(const int tag, const ShadowVie auto propsDiffer = PropsDiffer(uiRuntime, oldView, after); if (tagBefore == -1) { - propsDiffer.overrideTargetTransforms(strongThis->transformForNode_[tagAfter]); + propsDiffer.overrideTargetTransforms( + strongThis->transformForNode_[tagAfter]); } else { - LOG(INFO) << "start with overriden transforms " << tag << " " << tagBefore << " -> " << tagAfter; - propsDiffer.overrideSourceTransforms(strongThis->transformForNode_[tagBefore]); - propsDiffer.overrideTargetTransforms(strongThis->transformForNode_[tagAfter]); + LOG(INFO) << "start with overriden transforms " << tag << " " << tagBefore + << " -> " << tagAfter; + propsDiffer.overrideSourceTransforms( + strongThis->transformForNode_[tagBefore]); + propsDiffer.overrideTargetTransforms( + strongThis->transformForNode_[tagAfter]); } - + const auto &propsDiff = propsDiffer.computeDiff(uiRuntime); propsDiff.setProperty(uiRuntime, "windowWidth", window.width); propsDiff.setProperty(uiRuntime, "windowHeight", window.height); - - strongThis->layoutAnimationsManager_->startLayoutAnimation(uiRuntime, tag, LayoutAnimationType::SHARED_ELEMENT_TRANSITION, propsDiff); + + strongThis->layoutAnimationsManager_->startLayoutAnimation( + uiRuntime, + tag, + LayoutAnimationType::SHARED_ELEMENT_TRANSITION, + propsDiff); }); } -void LayoutAnimationsProxy::startProgressTransition(const int tag, const ShadowView &before, const ShadowView &after, SurfaceId surfaceId) const{ - - uiScheduler_->scheduleOnUI([weakThis = weak_from_this(), - before, - after, - surfaceId, - tag]() { - auto strongThis = weakThis.lock(); - if (!strongThis) { - return; - } +void LayoutAnimationsProxy::startProgressTransition( + const int tag, + const ShadowView &before, + const ShadowView &after, + SurfaceId surfaceId) const { + uiScheduler_->scheduleOnUI( + [weakThis = weak_from_this(), before, after, surfaceId, tag]() { + auto strongThis = weakThis.lock(); + if (!strongThis) { + return; + } - auto oldView = before; - Rect window{}; - { - auto &mutex = strongThis->mutex; - auto lock = std::unique_lock(mutex); - strongThis->createLayoutAnimation(ShadowViewMutation::InsertMutation(surfaceId, after, 1), oldView, surfaceId, tag); - window = strongThis->surfaceManager.getWindow(surfaceId); - } - }); + auto oldView = before; + Rect window{}; + { + auto &mutex = strongThis->mutex; + auto lock = std::unique_lock(mutex); + strongThis->createLayoutAnimation( + ShadowViewMutation::InsertMutation(surfaceId, after, 1), + oldView, + surfaceId, + tag); + window = strongThis->surfaceManager.getWindow(surfaceId); + } + }); } void LayoutAnimationsProxy::updateOngoingAnimationTarget( @@ -1384,17 +1558,17 @@ std::shared_ptr LayoutAnimationsProxy::cloneViewWithoutOpacity( return newView; } - std::shared_ptr LayoutAnimationsProxy::cloneViewWithOpacity( - facebook::react::ShadowViewMutation &mutation, - const PropsParserContext &propsParserContext) const { - auto newView = std::make_shared(mutation.newChildShadowView); - const auto& props = static_cast(*newView.get()->props); - folly::dynamic opacity = folly::dynamic::object("opacity", props.opacity); - auto newProps = getComponentDescriptorForShadowView(*newView).cloneProps( - propsParserContext, newView->props, RawProps(opacity)); - newView->props = newProps; - return newView; - } +std::shared_ptr LayoutAnimationsProxy::cloneViewWithOpacity( + facebook::react::ShadowViewMutation &mutation, + const PropsParserContext &propsParserContext) const { + auto newView = std::make_shared(mutation.newChildShadowView); + const auto &props = static_cast(*newView.get()->props); + folly::dynamic opacity = folly::dynamic::object("opacity", props.opacity); + auto newProps = getComponentDescriptorForShadowView(*newView).cloneProps( + propsParserContext, newView->props, RawProps(opacity)); + newView->props = newProps; + return newView; +} void LayoutAnimationsProxy::maybeRestoreOpacity( LayoutAnimation &layoutAnimation, diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h index 812f31104039..41418ada4470 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h @@ -6,16 +6,16 @@ #include #include +#include #include #include -#include #include +#include #include #include #include #include -#include namespace reanimated { @@ -40,27 +40,27 @@ struct LayoutAnimationsProxy mutable SurfaceManager surfaceManager; mutable std::unordered_set> deadNodes; mutable std::unordered_map leastRemoved; - mutable std::unordered_set activeTransitions_; - mutable Tag transitionTag_; - mutable double transitionProgress_; - mutable bool transitionUpdated_; - mutable TransitionState transitionState_ = NONE; - mutable std::unordered_map> topScreen; - mutable int myTag = 10002; - mutable std::vector sharedContainersToRemove_; - mutable std::unordered_map restoreMap_; - mutable std::vector tagsToRestore_; - mutable TransitionMap transitionMap_; - mutable Transitions transitions_; - mutable bool synchronized_ = true; - mutable std::vector entering_, layout_, exiting_; - std::shared_ptr sharedTransitionManager_; -// mutable std::unordered_map< -// mutable std::optional previousView; - mutable std::unordered_map> lightNodes_; - - mutable std::unordered_map transformForNode_; - + mutable std::unordered_set activeTransitions_; + mutable Tag transitionTag_; + mutable double transitionProgress_; + mutable bool transitionUpdated_; + mutable TransitionState transitionState_ = NONE; + mutable std::unordered_map> topScreen; + mutable int myTag = 10002; + mutable std::vector sharedContainersToRemove_; + mutable std::unordered_map restoreMap_; + mutable std::vector tagsToRestore_; + mutable TransitionMap transitionMap_; + mutable Transitions transitions_; + mutable bool synchronized_ = true; + mutable std::vector entering_, layout_, exiting_; + std::shared_ptr sharedTransitionManager_; + // mutable std::unordered_map< + // mutable std::optional previousView; + mutable std::unordered_map> lightNodes_; + + mutable std::unordered_map transformForNode_; + mutable std::vector finishedAnimationTags_; std::shared_ptr layoutAnimationsManager_; std::shared_ptr contextContainer_; @@ -73,59 +73,101 @@ struct LayoutAnimationsProxy std::shared_ptr contextContainer, jsi::Runtime &uiRuntime, const std::shared_ptr uiScheduler) - : sharedTransitionManager_(layoutAnimationsManager->sharedTransitionManager_), + : sharedTransitionManager_( + layoutAnimationsManager->sharedTransitionManager_), layoutAnimationsManager_(layoutAnimationsManager), contextContainer_(contextContainer), componentDescriptorRegistry_(componentDescriptorRegistry), uiRuntime_(uiRuntime), uiScheduler_(uiScheduler) { - lightNodes_[1] = std::make_shared(); - lightNodes_[11] = std::make_shared(); - - } + lightNodes_[1] = std::make_shared(); + lightNodes_[11] = std::make_shared(); + } void startEnteringAnimation(const int tag, const ShadowViewMutation &mutation) const; - void startExitingAnimation(const int tag, const ShadowViewMutation &mutation) const; + void startExitingAnimation(const int tag, const ShadowViewMutation &mutation) + const; void startLayoutAnimation(const int tag, const ShadowViewMutation &mutation) const; - void startSharedTransition(const int tag, const ShadowView &before, const ShadowView &after, SurfaceId surfaceId, const int tagBefore, const int tagAfter) - const; - void startProgressTransition(const int tag, const ShadowView &before, const ShadowView &after, SurfaceId surfaceId) - const; - void handleProgressTransition(ShadowViewMutationList &filteredMutations, const ShadowViewMutationList &mutations, const PropsParserContext &propsParserContext, SurfaceId surfaceId) const; - - void updateLightTree(const PropsParserContext& propsParserContext, const ShadowViewMutationList &mutations, ShadowViewMutationList& filteredMutations) const; - - void handleSharedTransitionsStart(const LightNode::Unshared &afterTopScreen, const LightNode::Unshared &beforeTopScreen, ShadowViewMutationList &filteredMutations, const ShadowViewMutationList &mutations, const PropsParserContext &propsParserContext, SurfaceId surfaceId) const; - - void cleanupSharedTransitions(ShadowViewMutationList &filteredMutations, const PropsParserContext &propsParserContext, SurfaceId surfaceId) const; - - void hideTransitioningViews(int index, ShadowViewMutationList &filteredMutations, const PropsParserContext &propsParserContext) const; - - + void startSharedTransition( + const int tag, + const ShadowView &before, + const ShadowView &after, + SurfaceId surfaceId, + const int tagBefore, + const int tagAfter) const; + void startProgressTransition( + const int tag, + const ShadowView &before, + const ShadowView &after, + SurfaceId surfaceId) const; + void handleProgressTransition( + ShadowViewMutationList &filteredMutations, + const ShadowViewMutationList &mutations, + const PropsParserContext &propsParserContext, + SurfaceId surfaceId) const; + + void updateLightTree( + const PropsParserContext &propsParserContext, + const ShadowViewMutationList &mutations, + ShadowViewMutationList &filteredMutations) const; + + void handleSharedTransitionsStart( + const LightNode::Unshared &afterTopScreen, + const LightNode::Unshared &beforeTopScreen, + ShadowViewMutationList &filteredMutations, + const ShadowViewMutationList &mutations, + const PropsParserContext &propsParserContext, + SurfaceId surfaceId) const; + + void cleanupSharedTransitions( + ShadowViewMutationList &filteredMutations, + const PropsParserContext &propsParserContext, + SurfaceId surfaceId) const; + + void hideTransitioningViews( + int index, + ShadowViewMutationList &filteredMutations, + const PropsParserContext &propsParserContext) const; + void transferConfigFromNativeID(const std::string nativeId, const int tag) const; std::optional progressLayoutAnimation( int tag, const jsi::Object &newStyle); std::optional endLayoutAnimation(int tag, bool shouldRemove); - std::optional onTransitionProgress(int tag, double progress, bool isClosing, bool isGoingForward, bool isSwiping); - std::optional onGestureCancel(); + std::optional onTransitionProgress( + int tag, + double progress, + bool isClosing, + bool isGoingForward, + bool isSwiping); + std::optional onGestureCancel(); void maybeCancelAnimation(const int tag) const; - - Tag findVisible(std::shared_ptr node,int& count) const; - - LightNode::Unshared findTopScreen(LightNode::Unshared node) const; - - void findSharedElementsOnScreen(const LightNode::Unshared &node, int index) const; - std::vector getAbsolutePositionsForRootPathView(const LightNode::Unshared &node) const; + Tag findVisible(std::shared_ptr node, int &count) const; + + LightNode::Unshared findTopScreen(LightNode::Unshared node) const; + + void findSharedElementsOnScreen(const LightNode::Unshared &node, int index) + const; - void parseParentTransforms(const LightNode::Unshared &node, const std::vector &absolutePositions) const; - react::Transform resolveTransform(const LayoutMetrics &layoutMetrics, const Transform &transform, const TransformOrigin &transformOrigin) const; - std::array getTranslateForTransformOrigin(float viewWidth, float viewHeight, const TransformOrigin &transformOrigin) const; + std::vector getAbsolutePositionsForRootPathView( + const LightNode::Unshared &node) const; + + void parseParentTransforms( + const LightNode::Unshared &node, + const std::vector &absolutePositions) const; + react::Transform resolveTransform( + const LayoutMetrics &layoutMetrics, + const Transform &transform, + const TransformOrigin &transformOrigin) const; + std::array getTranslateForTransformOrigin( + float viewWidth, + float viewHeight, + const TransformOrigin &transformOrigin) const; void handleRemovals( ShadowViewMutationList &filteredMutations, @@ -141,9 +183,9 @@ struct LayoutAnimationsProxy facebook::react::ShadowViewMutation &mutation, const PropsParserContext &propsParserContext) const; - std::shared_ptr cloneViewWithOpacity( - facebook::react::ShadowViewMutation &mutation, - const PropsParserContext &propsParserContext) const; + std::shared_ptr cloneViewWithOpacity( + facebook::react::ShadowViewMutation &mutation, + const PropsParserContext &propsParserContext) const; void maybeRestoreOpacity( LayoutAnimation &layoutAnimation, const jsi::Object &newStyle) const; @@ -163,7 +205,7 @@ struct LayoutAnimationsProxy ShadowViewMutationList &mutations) const; void endAnimationsRecursively( std::shared_ptr node, - int index, + int index, ShadowViewMutationList &mutations) const; void maybeDropAncestors( std::shared_ptr node, diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsUtils.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsUtils.h index e6d306dc147e..873042f4eb35 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsUtils.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsUtils.h @@ -19,7 +19,8 @@ struct Rect { struct Frame { std::optional x, y, width, height; - Frame(double x, double y, double width, double height): x(x), y(y), width(width), height(height) {} + Frame(double x, double y, double width, double height) + : x(x), y(y), width(width), height(height) {} Frame(jsi::Runtime &runtime, const jsi::Object &newStyle) { if (newStyle.hasProperty(runtime, "originX")) { x = newStyle.getProperty(runtime, "originX").asNumber(); diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/PropsDiffer.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/PropsDiffer.cpp index 06a0d8623d6a..78bd8025a016 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/PropsDiffer.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/PropsDiffer.cpp @@ -26,19 +26,14 @@ jsi::Object PropsDiffer::computeDiff(jsi::Runtime &runtime) { void PropsDiffer::overrideSourceTransforms(const Transform &transform) { sourceTransform_ = Transform::FromTransformOperation( - TransformOperation(TransformOperationType::Arbitrary), - {}, - transform - ); + TransformOperation(TransformOperationType::Arbitrary), {}, transform); needsOverrideSourceTransforms_ = true; } void PropsDiffer::overrideTargetTransforms(const Transform &transform) { targetTransform_ = Transform::FromTransformOperation( - TransformOperation(TransformOperationType::Arbitrary), - {}, - transform - );; + TransformOperation(TransformOperationType::Arbitrary), {}, transform); + ; needsOverrideTargetTransforms_ = true; } diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp index 0cbc61cad20f..80af24a31c01 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp @@ -345,7 +345,7 @@ jsi::Value ReanimatedModuleProxy::configureLayoutAnimationBatch( "[Reanimated] Layout animation config must be an object."); } auto sharedTag = item.getProperty(rt, "sharedTransitionTag"); - if (!sharedTag.isUndefined()){ + if (!sharedTag.isUndefined()) { batchItem.sharedTransitionTag = sharedTag.asString(rt).utf8(rt); } } @@ -592,8 +592,8 @@ bool ReanimatedModuleProxy::handleRawEvent( if (eventType.rfind("top", 0) == 0) { eventType = "on" + eventType.substr(3); } - - if (!strcmp(eventType.c_str(), "onTransitionProgress")){ + + if (!strcmp(eventType.c_str(), "onTransitionProgress")) { jsi::Runtime &rt = workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(); const auto &eventPayload = rawEvent.eventPayload; @@ -602,26 +602,26 @@ bool ReanimatedModuleProxy::handleRawEvent( auto closing = payload.getProperty(rt, "closing").asNumber(); auto goingForward = payload.getProperty(rt, "goingForward").asNumber(); auto swiping = payload.getProperty(rt, "swiping").asNumber(); - - auto surfaceId = layoutAnimationsProxy_->onTransitionProgress(tag, progress, closing, goingForward, swiping); - if (!surfaceId){ + + auto surfaceId = layoutAnimationsProxy_->onTransitionProgress( + tag, progress, closing, goingForward, swiping); + if (!surfaceId) { return false; } // TODO: enumerate -> visit uiManager_->getShadowTreeRegistry().enumerate( - [](const ShadowTree &shadowTree, bool&) { + [](const ShadowTree &shadowTree, bool &) { shadowTree.notifyDelegatesOfUpdates(); }); return false; - } else if (!strcmp(eventType.c_str(), "onGestureCancel")){ - + } else if (!strcmp(eventType.c_str(), "onGestureCancel")) { auto surfaceId = layoutAnimationsProxy_->onGestureCancel(); - if (!surfaceId){ + if (!surfaceId) { return false; } // TODO: enumerate -> visit uiManager_->getShadowTreeRegistry().enumerate( - [](const ShadowTree &shadowTree, bool&) { + [](const ShadowTree &shadowTree, bool &) { shadowTree.notifyDelegatesOfUpdates(); }); return false; @@ -735,7 +735,7 @@ void ReanimatedModuleProxy::performOperations() { shouldUpdateCssAnimations_ = false; -// TODO: use the SET flag when it's ready +// TODO: use the SET flag when it's ready #ifdef ANDROID if constexpr (false) { static const std::unordered_set synchronousProps = { diff --git a/packages/react-native-reanimated/src/layoutReanimation/SharedTransition.ts b/packages/react-native-reanimated/src/layoutReanimation/SharedTransition.ts index 5fe37c65353a..f852c1ae2be7 100644 --- a/packages/react-native-reanimated/src/layoutReanimation/SharedTransition.ts +++ b/packages/react-native-reanimated/src/layoutReanimation/SharedTransition.ts @@ -1,5 +1,8 @@ 'use strict'; -import type { ILayoutAnimationBuilder, LayoutAnimationFunction } from '../commonTypes'; +import type { + ILayoutAnimationBuilder, + LayoutAnimationFunction, +} from '../commonTypes'; import type { BaseAnimationBuilder } from './animationBuilder'; import { ComplexAnimationBuilder } from './animationBuilder'; @@ -28,8 +31,8 @@ export class SharedTransition target: Record; }; const animationFactory = (value: number | string) => { - return delayFunction(delay, animation(value, config)) - } + return delayFunction(delay, animation(value, config)); + }; const initialValues: any = {}; const animations: any = {}; for (const key in values.source) { @@ -38,20 +41,24 @@ export class SharedTransition const target = values.target[key]; if (Array.isArray(target)) { if (key === 'transform') { - animations[key] = target.map((item: Record) => { - const key = Object.keys(item)[0]; - return { - [key]: animationFactory(item[key]), - }; - }); + animations[key] = target.map( + (item: Record) => { + const key = Object.keys(item)[0]; + return { + [key]: animationFactory(item[key]), + }; + } + ); } else if (key === 'boxShadow') { - animations[key] = target.map((item: Record) => { - const boxShadow: Record = {}; - for (const shadowKey of Object.keys(item)) { - boxShadow[shadowKey] = animationFactory(item[shadowKey]); + animations[key] = target.map( + (item: Record) => { + const boxShadow: Record = {}; + for (const shadowKey of Object.keys(item)) { + boxShadow[shadowKey] = animationFactory(item[shadowKey]); + } + return boxShadow; } - return boxShadow; - }); + ); } else if (key === 'transformOrigin') { animations[key] = target.map(animationFactory); } else { From 6e4517c82e325686c5e804583ada4bf605f2ded0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Mon, 29 Sep 2025 11:32:35 +0200 Subject: [PATCH 48/90] purge logs --- .../LayoutAnimationsProxy.cpp | 134 +----------------- .../LayoutAnimations/LayoutAnimationsProxy.h | 2 - 2 files changed, 2 insertions(+), 134 deletions(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index 4ba5989d5e30..7d8a60d5c38b 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -29,12 +29,6 @@ std::optional LayoutAnimationsProxy::pullTransaction( 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 - LOG(INFO) << "pullTransaction"; auto lock = std::unique_lock(mutex); ReanimatedSystraceSection d("pullTransaction"); PropsParserContext propsParserContext{surfaceId, *contextContainer_}; @@ -126,31 +120,6 @@ std::optional LayoutAnimationsProxy::pullTransaction( surfaceId, transactionNumber, std::move(filteredMutations), telemetry}; } -Tag LayoutAnimationsProxy::findVisible( - std::shared_ptr node, - int &count) const { - // auto group = - // sharedTransitionManager_->groups_[sharedTransitionManager_->tagToName_[node->current.tag]]; - // while (node != nullptr){ - // if (!strcmp(node->current.componentName, "RNSScreenStack")){ - // - // } - // node = node->parent.lock(); - // } - int c = count; - if (!strcmp(node->current.componentName, "RNSScreen")) { - LOG(INFO) << c << " begin screen tag: " << node->current.tag << std::endl; - count++; - } - for (auto &child : node->children) { - findVisible(child, count); - } - if (!strcmp(node->current.componentName, "RNSScreen")) { - LOG(INFO) << c << " end screen" << std::endl; - } - return -1; -} - LightNode::Unshared LayoutAnimationsProxy::findTopScreen( LightNode::Unshared node) const { LightNode::Unshared result = nullptr; @@ -211,11 +180,6 @@ void LayoutAnimationsProxy::findSharedElementsOnScreen( } } -// struct S{ -// const react::Size frameSize{}; -// react::Point contentOffset; -// }; - std::vector LayoutAnimationsProxy::getAbsolutePositionsForRootPathView( const LightNode::Unshared &node) const { @@ -227,8 +191,6 @@ LayoutAnimationsProxy::getAbsolutePositionsForRootPathView( auto state = std::static_pointer_cast( currentNode->current.state); auto data = state->getData(); - // LOG(INFO) << node->current.tag << " content offset:" << - // data.contentOffset.x << " " << data.contentOffset.y; viewPosition -= data.contentOffset; } if (!strcmp(currentNode->current.componentName, "RNSScreen") && @@ -238,10 +200,6 @@ LayoutAnimationsProxy::getAbsolutePositionsForRootPathView( float headerHeight = parent->current.layoutMetrics.frame.size.height - currentNode->current.layoutMetrics.frame.size.height; viewPosition.y += headerHeight; - // auto state = std::reinterpret_pointer_cast>(currentNode->current.state); LOG(INFO) - // <<"state: " <getData().contentOffset.y; viewPosition.y - // += state->getData().contentOffset.y; } } viewPosition += currentNode->current.layoutMetrics.frame.origin; @@ -389,7 +347,6 @@ void LayoutAnimationsProxy::handleProgressTransition( const ShadowViewMutationList &mutations, const PropsParserContext &propsParserContext, SurfaceId surfaceId) const { - LOG(INFO) << "Transition state: " << transitionState_; if (!transitionUpdated_) { return; } @@ -401,10 +358,6 @@ void LayoutAnimationsProxy::handleProgressTransition( auto beforeTopScreen = topScreen[surfaceId]; auto afterTopScreen = lightNodes_[transitionTag_]; if (beforeTopScreen && afterTopScreen) { - LOG(INFO) << "start progress transition: " - << beforeTopScreen->current.tag << " -> " - << afterTopScreen->current.tag; - findSharedElementsOnScreen(beforeTopScreen, 0); findSharedElementsOnScreen(afterTopScreen, 1); @@ -535,6 +488,7 @@ void LayoutAnimationsProxy::updateLightTree( const PropsParserContext &propsParserContext, const ShadowViewMutationList &mutations, ShadowViewMutationList &filteredMutations) const { + ReanimatedSystraceSection s("updateLightTree"); std::unordered_set moved, deleted; for (auto it = mutations.rbegin(); it != mutations.rend(); it++) { const auto &mutation = *it; @@ -645,7 +599,6 @@ void LayoutAnimationsProxy::updateLightTree( if (node->intent == TO_DELETE && parent->intent != TO_DELETE) { exiting_.push_back(node); - LOG(INFO) << "remove3 " << node->current.tag; if (parent->children[mutation.index]->current.tag == mutation.oldChildShadowView.tag) { filteredMutations.push_back(mutation); @@ -654,7 +607,6 @@ void LayoutAnimationsProxy::updateLightTree( } parent->children.erase(parent->children.begin() + mutation.index); } else if (node->intent != TO_DELETE) { - LOG(INFO) << "remove4 " << node->current.tag; if (parent->children[mutation.index]->current.tag == mutation.oldChildShadowView.tag) { filteredMutations.push_back(mutation); @@ -671,32 +623,6 @@ void LayoutAnimationsProxy::updateLightTree( } } -void printTree(LightNode::Unshared &node, int level) { - if (!(strcmp(node->current.componentName, "RNSScreen"))) { -// bool isActive = false; -#ifdef ANDROID - float f = - node->current.props->rawProps.getDefault("activityState", 0).asDouble(); -// isActive = f == 2.0f; -#else - float f = - std::static_pointer_cast(node->current.props) - ->activityState; -#endif - LOG(INFO) << "screen start (activityState: " << f << ") " - << node->current.tag << " " << level; - } else { - // LOG(INFO) << node->current.componentName << " " << - // node->current.tag << " " << level; - } - for (auto &child : node->children) { - printTree(child, level + 1); - } - if (!(strcmp(node->current.componentName, "RNSScreen"))) { - LOG(INFO) << "screen end" << " " << node->current.tag; - } -} - void LayoutAnimationsProxy::handleSharedTransitionsStart( const LightNode::Unshared &afterTopScreen, const LightNode::Unshared &beforeTopScreen, @@ -709,12 +635,7 @@ void LayoutAnimationsProxy::handleSharedTransitionsStart( if (beforeTopScreen && afterTopScreen && beforeTopScreen->current.tag != afterTopScreen->current.tag) { - LOG(INFO) << "different tags"; - LOG(INFO) << "start transition: " << beforeTopScreen->current.tag - << " -> " << afterTopScreen->current.tag; - for (auto &[sharedTag, transition] : transitions_) { - LOG(INFO) << "sharedTag: " << sharedTag; const auto &[before, after] = transition.snapshot; const auto &[beforeParentTag, afterParentTag] = transition.parentTag; @@ -791,7 +712,6 @@ void LayoutAnimationsProxy::handleSharedTransitionsStart( } else if ( mutations.size() && beforeTopScreen && afterTopScreen && beforeTopScreen->current.tag == afterTopScreen->current.tag) { - LOG(INFO) << "same tag"; for (auto &[sharedTag, transition] : transitions_) { const auto &[_, after] = transition.snapshot; @@ -839,7 +759,6 @@ void LayoutAnimationsProxy::cleanupSharedTransitions( ShadowViewMutation::RemoveMutation(surfaceId, child->current, i)); filteredMutations.push_back( ShadowViewMutation::DeleteMutation(child->current)); - LOG(INFO) << "delete container " << tag; root->children.erase(root->children.begin() + i); } } @@ -865,9 +784,6 @@ void LayoutAnimationsProxy::hideTransitioningViews( 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); @@ -905,10 +821,6 @@ std::optional LayoutAnimationsProxy::progressLayoutAnimation( 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); @@ -960,9 +872,6 @@ std::optional LayoutAnimationsProxy::onTransitionProgress( bool isSwiping) { auto lock = std::unique_lock(mutex); transitionUpdated_ = true; - // LOG(INFO) << "notifyTransitionProgress ("<< tag <<"): " << progress << ", - // closing: " << isClosing << ", goingForward: " << isGoingForward << ", - // isSwiping: " <current.tag); filteredMutations.push_back( ShadowViewMutation::DeleteMutation(node->current)); -#ifdef LAYOUT_ANIMATIONS_LOGS - LOG(INFO) << "delete " << node->tag << std::endl; -#endif } } @@ -1072,8 +978,6 @@ void LayoutAnimationsProxy::addOngoingAnimations( } updateLayoutMetrics(newView->layoutMetrics, updateValues.frame); - LOG(INFO) << "(addOngoing) " << tag; - mutations.push_back(ShadowViewMutation::UpdateMutation( *layoutAnimation.currentView, *newView, layoutAnimation.parentTag)); layoutAnimation.currentView = newView; @@ -1098,13 +1002,8 @@ void LayoutAnimationsProxy::endAnimationsRecursively( } } node->children.clear(); - LOG(INFO) << "remove1 " << node->current.tag; mutations.push_back(ShadowViewMutation::RemoveMutation( node->parent.lock()->current.tag, node->current, index)); -// nodeForTag_.erase(node->tag); -#ifdef LAYOUT_ANIMATIONS_LOGS - LOG(INFO) << "delete " << node->tag << std::endl; -#endif mutations.push_back(ShadowViewMutation::DeleteMutation(node->current)); } @@ -1133,13 +1032,9 @@ void LayoutAnimationsProxy::maybeDropAncestors( auto pp = parent->parent.lock(); for (int i = 0; i < pp->children.size(); i++) { if (pp->children[i]->current.tag == parent->current.tag) { - LOG(INFO) << "remove2 " << parent->current.tag << ", " << parent->state; cleanupMutations.push_back(ShadowViewMutation::RemoveMutation( pp->current.tag, parent->current, i)); maybeCancelAnimation(parent->current.tag); -#ifdef LAYOUT_ANIMATIONS_LOGS - LOG(INFO) << "delete " << node->tag << std::endl; -#endif cleanupMutations.push_back( ShadowViewMutation::DeleteMutation(parent->current)); maybeDropAncestors(parent->parent.lock(), parent, cleanupMutations); @@ -1184,11 +1079,6 @@ bool LayoutAnimationsProxy::startAnimationsRecursively( for (auto it = node->children.rbegin(); it != node->children.rend(); it++) { index--; auto &subNode = *it; -#ifdef LAYOUT_ANIMATIONS_LOGS - LOG(INFO) << "child " << subNode->tag << " " - << " " << shouldAnimate << " " - << shouldRemoveSubviewsWithoutAnimations << std::endl; -#endif if (subNode->state != UNDEFINED) { if (shouldAnimate && subNode->state != DEAD) { hasAnimatedChildren = true; @@ -1202,23 +1092,14 @@ bool LayoutAnimationsProxy::startAnimationsRecursively( shouldAnimate, isScreenPop, mutations)) { -#ifdef LAYOUT_ANIMATIONS_LOGS - LOG(INFO) << "child " << subNode->tag - << " start animations returned true " << std::endl; -#endif hasAnimatedChildren = true; } else if (shouldRemoveSubviewsWithoutAnimations) { maybeCancelAnimation(subNode->current.tag); - LOG(INFO) << "remove " << subNode->current.tag << " from " - << node->current.tag << " at " << index; mutations.push_back(ShadowViewMutation::RemoveMutation( node->current.tag, subNode->current, index)); toBeRemoved.push_back(subNode); subNode->state = DELETED; -// nodeForTag_.erase(subNode->tag); -#ifdef LAYOUT_ANIMATIONS_LOGS - LOG(INFO) << "delete " << subNode->tag << std::endl; -#endif + // nodeForTag_.erase(subNode->tag); mutations.push_back(ShadowViewMutation::DeleteMutation(subNode->current)); } else { subNode->state = WAITING; @@ -1278,9 +1159,6 @@ void LayoutAnimationsProxy::createLayoutAnimation( void LayoutAnimationsProxy::startEnteringAnimation( const int tag, const 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); @@ -1330,9 +1208,6 @@ void LayoutAnimationsProxy::startEnteringAnimation( void LayoutAnimationsProxy::startExitingAnimation( const int tag, const 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( @@ -1372,9 +1247,6 @@ void LayoutAnimationsProxy::startExitingAnimation( 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(), @@ -1459,8 +1331,6 @@ void LayoutAnimationsProxy::startSharedTransition( propsDiffer.overrideTargetTransforms( strongThis->transformForNode_[tagAfter]); } else { - LOG(INFO) << "start with overriden transforms " << tag << " " << tagBefore - << " -> " << tagAfter; propsDiffer.overrideSourceTransforms( strongThis->transformForNode_[tagBefore]); propsDiffer.overrideTargetTransforms( diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h index 41418ada4470..49280dc1638d 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h @@ -147,8 +147,6 @@ struct LayoutAnimationsProxy void maybeCancelAnimation(const int tag) const; - Tag findVisible(std::shared_ptr node, int &count) const; - LightNode::Unshared findTopScreen(LightNode::Unshared node) const; void findSharedElementsOnScreen(const LightNode::Unshared &node, int index) From e25842d97c187a2125698e394626dd1c13c58077 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Mon, 29 Sep 2025 15:26:38 +0200 Subject: [PATCH 49/90] cleanup 1 --- .../LayoutAnimationsProxy.cpp | 175 ++++++++---------- .../LayoutAnimations/LayoutAnimationsProxy.h | 14 +- 2 files changed, 83 insertions(+), 106 deletions(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index 7d8a60d5c38b..e7df692345ec 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -34,7 +34,6 @@ std::optional LayoutAnimationsProxy::pullTransaction( PropsParserContext propsParserContext{surfaceId, *contextContainer_}; ShadowViewMutationList filteredMutations; std::vector> roots; - std::unordered_map movedViews; bool isInTransition = transitionState_; if (isInTransition) { @@ -45,7 +44,7 @@ std::optional LayoutAnimationsProxy::pullTransaction( auto actualTop = topScreen[surfaceId]; updateLightTree(propsParserContext, mutations, filteredMutations); auto reactTop = findTopScreen(lightNodes_[surfaceId]); - if (reactTop->current.tag == actualTop->current.tag) { + if (reactTop == actualTop) { synchronized_ = true; } } else { @@ -63,8 +62,7 @@ std::optional LayoutAnimationsProxy::pullTransaction( if (afterTopScreen) { findSharedElementsOnScreen(afterTopScreen, 1); } - bool shouldTransitionStart = beforeTopScreen && afterTopScreen && - beforeTopScreen->current.tag != afterTopScreen->current.tag; + bool shouldTransitionStart = beforeTopScreen && afterTopScreen && beforeTopScreen != afterTopScreen; if (shouldTransitionStart) { std::vector temp; @@ -84,16 +82,10 @@ std::optional LayoutAnimationsProxy::pullTransaction( surfaceId); for (auto &node : entering_) { - startEnteringAnimation( - node->current.tag, - ShadowViewMutation::InsertMutation( - node->parent.lock()->current.tag, node->current, -1)); + startEnteringAnimation(node); } for (auto &node : layout_) { - startLayoutAnimation( - node->current.tag, - ShadowViewMutation::UpdateMutation( - node->previous, node->current, node->parent.lock()->current.tag)); + startLayoutAnimation(node); } entering_.clear(); layout_.clear(); @@ -416,9 +408,9 @@ void LayoutAnimationsProxy::handleProgressTransition( for (auto tag : activeTransitions_) { auto layoutAnimation = layoutAnimations_[tag]; auto &updateMap = - surfaceManager.getUpdateMap(layoutAnimation.finalView->surfaceId); - auto before = layoutAnimation.startView->layoutMetrics.frame; - auto after = layoutAnimation.finalView->layoutMetrics.frame; + surfaceManager.getUpdateMap(layoutAnimation.finalView.surfaceId); + auto before = layoutAnimation.startView.layoutMetrics.frame; + auto after = layoutAnimation.finalView.layoutMetrics.frame; auto x = before.origin.x + transitionProgress_ * (after.origin.x - before.origin.x); auto y = before.origin.y + @@ -429,9 +421,9 @@ void LayoutAnimationsProxy::handleProgressTransition( transitionProgress_ * (after.size.height - before.size.height); auto beforeProps = std::static_pointer_cast( - layoutAnimation.startView->props); + layoutAnimation.startView.props); auto afterProps = std::static_pointer_cast( - layoutAnimation.finalView->props); + layoutAnimation.finalView.props); auto beforeRadius = beforeProps->borderRadii.all.value_or(ValueUnit(0, UnitType::Point)) .value; @@ -451,10 +443,10 @@ void LayoutAnimationsProxy::handleProgressTransition( auto rawProps = RawProps(std::move(d)); auto newProps = - getComponentDescriptorForShadowView(*layoutAnimation.finalView) + getComponentDescriptorForShadowView(layoutAnimation.finalView) .cloneProps( propsParserContext, - layoutAnimation.finalView->props, + layoutAnimation.finalView.props, std::move(rawProps)); #endif @@ -722,9 +714,9 @@ void LayoutAnimationsProxy::handleSharedTransitionsStart( continue; } auto &la = layoutAnimations_[fakeTag]; - if (la.finalView->layoutMetrics != copy.layoutMetrics) { + if (la.finalView.layoutMetrics != copy.layoutMetrics) { startSharedTransition( - fakeTag, copy, copy, surfaceId, la.finalView->tag, after.tag); + fakeTag, copy, copy, surfaceId, la.finalView.tag, after.tag); } } } @@ -799,23 +791,23 @@ std::optional LayoutAnimationsProxy::progressLayoutAnimation( std::make_shared(uiRuntime_, jsi::Value(uiRuntime_, newStyle)); PropsParserContext propsParserContext{ - layoutAnimation.finalView->surfaceId, *contextContainer_}; + layoutAnimation.finalView.surfaceId, *contextContainer_}; #ifdef RN_SERIALIZABLE_STATE rawProps = std::make_shared(folly::dynamic::merge( - layoutAnimation.finalView->props->rawProps, (folly::dynamic)*rawProps)); + layoutAnimation.finalView.props->rawProps, (folly::dynamic)*rawProps)); #endif auto newProps = - getComponentDescriptorForShadowView(*layoutAnimation.finalView) + getComponentDescriptorForShadowView(layoutAnimation.finalView) .cloneProps( propsParserContext, - layoutAnimation.finalView->props, + layoutAnimation.finalView.props, std::move(*rawProps)); auto &updateMap = - surfaceManager.getUpdateMap(layoutAnimation.finalView->surfaceId); + surfaceManager.getUpdateMap(layoutAnimation.finalView.surfaceId); updateMap.insert_or_assign( tag, UpdateValues{newProps, Frame(uiRuntime_, newStyle)}); - return layoutAnimation.finalView->surfaceId; + return layoutAnimation.finalView.surfaceId; } std::optional LayoutAnimationsProxy::endLayoutAnimation( @@ -839,7 +831,7 @@ std::optional LayoutAnimationsProxy::endLayoutAnimation( return {}; } finishedAnimationTags_.push_back(tag); - auto surfaceId = layoutAnimation.finalView->surfaceId; + auto surfaceId = layoutAnimation.finalView.surfaceId; auto &updateMap = surfaceManager.getUpdateMap(surfaceId); layoutAnimations_.erase(tag); updateMap.erase(tag); @@ -923,7 +915,7 @@ void LayoutAnimationsProxy::handleRemovals( // TODO: handle this better auto current = node->current; if (layoutAnimations_.contains(node->current.tag)) { - current = *layoutAnimations_.at(node->current.tag).currentView; + current = layoutAnimations_.at(node->current.tag).currentView; } filteredMutations.push_back(ShadowViewMutation::InsertMutation( parent->current.tag, current, parent->children.size())); @@ -972,14 +964,14 @@ void LayoutAnimationsProxy::addOngoingAnimations( auto &layoutAnimation = layoutAnimationIt->second; - auto newView = std::make_shared(*layoutAnimation.finalView); + auto newView = layoutAnimation.finalView; if (updateValues.newProps) { - newView->props = updateValues.newProps; + newView.props = updateValues.newProps; } - updateLayoutMetrics(newView->layoutMetrics, updateValues.frame); + updateLayoutMetrics(newView.layoutMetrics, updateValues.frame); mutations.push_back(ShadowViewMutation::UpdateMutation( - *layoutAnimation.currentView, *newView, layoutAnimation.parentTag)); + layoutAnimation.currentView, newView, layoutAnimation.parentTag)); layoutAnimation.currentView = newView; } updateMap.clear(); @@ -1114,10 +1106,7 @@ bool LayoutAnimationsProxy::startAnimationsRecursively( if (hasExitAnimation) { node->state = ANIMATING; - startExitingAnimation( - node->current.tag, - ShadowViewMutation::RemoveMutation( - node->parent.lock()->current.tag, node->current, 0)); + startExitingAnimation(node); } else { // layoutAnimationsManager_->clearLayoutAnimationConfig(node->tag); } @@ -1129,67 +1118,63 @@ bool LayoutAnimationsProxy::shouldOverridePullTransaction() const { return true; } -void LayoutAnimationsProxy::createLayoutAnimation( - const ShadowViewMutation &mutation, - ShadowView &oldView, - const SurfaceId &surfaceId, - const int tag) const { - int count = 1; +ShadowView LayoutAnimationsProxy::createLayoutAnimation(ShadowView &before, const ShadowView& after, const Tag parentTag) const { + auto count = 1; + const auto tag = after.tag; auto layoutAnimationIt = layoutAnimations_.find(tag); - + auto& oldView = before; + if (layoutAnimationIt != layoutAnimations_.end()) { auto &layoutAnimation = layoutAnimationIt->second; - oldView = *layoutAnimation.currentView; + 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); - auto startView = std::make_shared(oldView); + auto finalView = after; + auto currentView = oldView; + auto startView = oldView; layoutAnimations_.insert_or_assign( tag, LayoutAnimation{ - finalView, currentView, startView, mutation.parentTag, {}, count}); + finalView, currentView, startView, parentTag, {}, count}); + + return oldView; } -void LayoutAnimationsProxy::startEnteringAnimation( - const int tag, - const ShadowViewMutation &mutation) const { - auto finalView = std::make_shared(mutation.newChildShadowView); - auto current = std::make_shared(mutation.newChildShadowView); +void LayoutAnimationsProxy::startEnteringAnimation(const LightNode::Unshared& node) const { + auto newChildShadowView = node->current; + auto finalView = newChildShadowView; + auto currentView = newChildShadowView; - auto &viewProps = - static_cast(*mutation.newChildShadowView.props); - auto opacity = viewProps.opacity; + const auto &props = newChildShadowView.props; + auto &viewProps = static_cast(*props); + const auto opacity = viewProps.opacity; + const auto parentTag = node->parent.lock()->current.tag; uiScheduler_->scheduleOnUI([weakThis = weak_from_this(), finalView, - current, - mutation, - opacity, - tag]() { + currentView, + newChildShadowView, + parentTag, + opacity]() { auto strongThis = weakThis.lock(); if (!strongThis) { return; } Rect window{}; + const auto tag = newChildShadowView.tag; { auto &mutex = strongThis->mutex; auto lock = std::unique_lock(mutex); strongThis->layoutAnimations_.insert_or_assign( tag, - LayoutAnimation{ - finalView, current, nullptr, mutation.parentTag, opacity}); - window = strongThis->surfaceManager.getWindow( - mutation.newChildShadowView.surfaceId); + LayoutAnimation{newChildShadowView, newChildShadowView, newChildShadowView, parentTag, opacity}); + window = strongThis->surfaceManager.getWindow(newChildShadowView.surfaceId); } - Snapshot values(mutation.newChildShadowView, window); + Snapshot values(newChildShadowView, window); auto &uiRuntime = strongThis->uiRuntime_; jsi::Object yogaValues(uiRuntime); yogaValues.setProperty(uiRuntime, "targetOriginX", values.x); @@ -1205,24 +1190,25 @@ void LayoutAnimationsProxy::startEnteringAnimation( }); } -void LayoutAnimationsProxy::startExitingAnimation( - const int tag, - const ShadowViewMutation &mutation) const { - auto surfaceId = mutation.oldChildShadowView.surfaceId; +void LayoutAnimationsProxy::startExitingAnimation(const LightNode::Unshared& node) const { + auto& oldChildShadowView = node->current; + const auto surfaceId = oldChildShadowView.surfaceId; + const auto tag = oldChildShadowView.tag; + const auto parentTag = node->parent.lock()->current.tag; uiScheduler_->scheduleOnUI( - [weakThis = weak_from_this(), tag, mutation, surfaceId]() { + [weakThis = weak_from_this(), tag, parentTag, oldChildShadowView, surfaceId]() { auto strongThis = weakThis.lock(); if (!strongThis) { return; } - auto oldView = mutation.oldChildShadowView; + auto oldView = oldChildShadowView; Rect window{}; { auto &mutex = strongThis->mutex; auto lock = std::unique_lock(mutex); - strongThis->createLayoutAnimation(mutation, oldView, surfaceId, tag); + oldView = strongThis->createLayoutAnimation(oldView, oldView, parentTag); window = strongThis->surfaceManager.getWindow(surfaceId); } @@ -1244,31 +1230,35 @@ void LayoutAnimationsProxy::startExitingAnimation( }); } -void LayoutAnimationsProxy::startLayoutAnimation( - const int tag, - const ShadowViewMutation &mutation) const { - auto surfaceId = mutation.oldChildShadowView.surfaceId; +void LayoutAnimationsProxy::startLayoutAnimation(const LightNode::Unshared& node) const { + auto oldChildShadowView = node->previous; + auto newChildShadowView = node->current; + auto surfaceId = oldChildShadowView.surfaceId; + const auto tag = oldChildShadowView.tag; + const auto parentTag = node->parent.lock()->current.tag; uiScheduler_->scheduleOnUI([weakThis = weak_from_this(), - mutation, surfaceId, + oldChildShadowView, + newChildShadowView, + parentTag, tag]() { auto strongThis = weakThis.lock(); if (!strongThis) { return; } - auto oldView = mutation.oldChildShadowView; + auto oldView = oldChildShadowView; Rect window{}; { auto &mutex = strongThis->mutex; auto lock = std::unique_lock(mutex); - strongThis->createLayoutAnimation(mutation, oldView, surfaceId, tag); + oldView = strongThis->createLayoutAnimation(oldView, newChildShadowView, parentTag); window = strongThis->surfaceManager.getWindow(surfaceId); } Snapshot currentValues(oldView, window); - Snapshot targetValues(mutation.newChildShadowView, window); + Snapshot targetValues(newChildShadowView, window); auto &uiRuntime = strongThis->uiRuntime_; jsi::Object yogaValues(uiRuntime); @@ -1316,11 +1306,7 @@ void LayoutAnimationsProxy::startSharedTransition( { auto &mutex = strongThis->mutex; auto lock = std::unique_lock(mutex); - strongThis->createLayoutAnimation( - ShadowViewMutation::InsertMutation(surfaceId, after, 1), - oldView, - surfaceId, - tag); + oldView = strongThis->createLayoutAnimation(oldView, after, surfaceId); window = strongThis->surfaceManager.getWindow(surfaceId); } @@ -1356,7 +1342,7 @@ void LayoutAnimationsProxy::startProgressTransition( const ShadowView &after, SurfaceId surfaceId) const { uiScheduler_->scheduleOnUI( - [weakThis = weak_from_this(), before, after, surfaceId, tag]() { + [weakThis = weak_from_this(), before, after, surfaceId]() { auto strongThis = weakThis.lock(); if (!strongThis) { return; @@ -1367,11 +1353,7 @@ void LayoutAnimationsProxy::startProgressTransition( { auto &mutex = strongThis->mutex; auto lock = std::unique_lock(mutex); - strongThis->createLayoutAnimation( - ShadowViewMutation::InsertMutation(surfaceId, after, 1), - oldView, - surfaceId, - tag); + oldView = strongThis->createLayoutAnimation(oldView, after, surfaceId); window = strongThis->surfaceManager.getWindow(surfaceId); } }); @@ -1380,8 +1362,7 @@ void LayoutAnimationsProxy::startProgressTransition( void LayoutAnimationsProxy::updateOngoingAnimationTarget( const int tag, const ShadowViewMutation &mutation) const { - layoutAnimations_[tag].finalView = - std::make_shared(mutation.newChildShadowView); + layoutAnimations_[tag].finalView = mutation.newChildShadowView; } void LayoutAnimationsProxy::maybeCancelAnimation(const int tag) const { diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h index 49280dc1638d..7684984077e7 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h @@ -24,7 +24,7 @@ class ReanimatedModuleProxy; using namespace facebook; struct LayoutAnimation { - std::shared_ptr finalView, currentView, startView; + ShadowView finalView, currentView, startView; Tag parentTag; std::optional opacity; int count = 1; @@ -84,11 +84,11 @@ struct LayoutAnimationsProxy lightNodes_[11] = std::make_shared(); } - void startEnteringAnimation(const int tag, const ShadowViewMutation &mutation) + void startEnteringAnimation(const LightNode::Unshared& node) const; - void startExitingAnimation(const int tag, const ShadowViewMutation &mutation) + void startExitingAnimation(const LightNode::Unshared& node) const; - void startLayoutAnimation(const int tag, const ShadowViewMutation &mutation) + void startLayoutAnimation(const LightNode::Unshared& node) const; void startSharedTransition( const int tag, @@ -189,11 +189,7 @@ struct LayoutAnimationsProxy const jsi::Object &newStyle) const; void maybeUpdateWindowDimensions( const facebook::react::ShadowViewMutation &mutation) const; - void createLayoutAnimation( - const ShadowViewMutation &mutation, - ShadowView &oldView, - const SurfaceId &surfaceId, - const int tag) const; + ShadowView createLayoutAnimation(ShadowView &before, const ShadowView& after, const Tag parentTag) const; bool startAnimationsRecursively( std::shared_ptr node, From 7a27152a354d59181043e4641695e612b1e99bfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Mon, 29 Sep 2025 15:44:29 +0200 Subject: [PATCH 50/90] Reorder --- .../LayoutAnimationsProxy.cpp | 930 +++++++++--------- 1 file changed, 471 insertions(+), 459 deletions(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index e7df692345ec..d038bcafed04 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -24,6 +24,8 @@ using ScrollState = ConcreteState; namespace reanimated { +// MARK: MountingOverrideDelegate + std::optional LayoutAnimationsProxy::pullTransaction( SurfaceId surfaceId, MountingTransaction::Number transactionNumber, @@ -112,6 +114,153 @@ std::optional LayoutAnimationsProxy::pullTransaction( surfaceId, transactionNumber, std::move(filteredMutations), telemetry}; } +bool LayoutAnimationsProxy::shouldOverridePullTransaction() const { + return true; +} + +// MARK: Light Tree + +void LayoutAnimationsProxy::updateLightTree( + const PropsParserContext &propsParserContext, + const ShadowViewMutationList &mutations, + ShadowViewMutationList &filteredMutations) const { + ReanimatedSystraceSection s("updateLightTree"); + std::unordered_set moved, deleted; + for (auto it = mutations.rbegin(); it != mutations.rend(); it++) { + const auto &mutation = *it; + switch (mutation.type) { + case ShadowViewMutation::Delete: { + deleted.insert(mutation.oldChildShadowView.tag); + break; + } + case ShadowViewMutation::Insert: { + moved.insert(mutation.newChildShadowView.tag); + break; + } + case ShadowViewMutation::Remove: { + const auto tag = mutation.oldChildShadowView.tag; + if (deleted.contains(tag)) { + lightNodes_[tag]->intent = TO_DELETE; + } else if (moved.contains(tag)) { + lightNodes_[tag]->intent = TO_MOVE; + } + break; + } + default: { + } + } + } + + for (auto &mutation : mutations) { + maybeUpdateWindowDimensions(mutation); + switch (mutation.type) { + case ShadowViewMutation::Update: { + auto &node = lightNodes_[mutation.newChildShadowView.tag]; + if (!node) { + node = std::make_shared(); + } + node->previous = mutation.oldChildShadowView; +#ifdef ANDROID + if (node->current.props) { + // on android rawProps are used to store the diffed props + // so we need to merge them + // this should soon be replaced in RN with Props 2.0 (the diffing will + // be done at the end of the pipeline) + auto ¤tRawProps = node->current.props->rawProps; + auto mergedRawProps = folly::dynamic::merge( + currentRawProps, mutation.newChildShadowView.props->rawProps); + node->current = mutation.newChildShadowView; + node->current.props = + getComponentDescriptorForShadowView(node->current) + .cloneProps( + propsParserContext, + mutation.newChildShadowView.props, + RawProps(mergedRawProps)); + } else { + node->current = mutation.newChildShadowView; + } +#else + node->current = mutation.newChildShadowView; +#endif + auto tag = mutation.newChildShadowView.tag; + if (layoutAnimationsManager_->hasLayoutAnimation(tag, LAYOUT)) { + layout_.push_back(node); + } else { + filteredMutations.push_back(mutation); + } + break; + } + case ShadowViewMutation::Create: { + auto &node = lightNodes_[mutation.newChildShadowView.tag]; + node = std::make_shared(); + node->current = mutation.newChildShadowView; + filteredMutations.push_back(mutation); + break; + } + case ShadowViewMutation::Delete: { + // lightNodes_.erase(mutation.oldChildShadowView.tag); + break; + } + case ShadowViewMutation::Insert: { + transferConfigFromNativeID( + mutation.newChildShadowView.props->nativeId, + mutation.newChildShadowView.tag); + auto &node = lightNodes_[mutation.newChildShadowView.tag]; + auto &parent = lightNodes_[mutation.parentTag]; + parent->children.insert( + parent->children.begin() + mutation.index, node); + node->parent = parent; + const auto tag = mutation.newChildShadowView.tag; + if (node->intent == TO_MOVE && + layoutAnimationsManager_->hasLayoutAnimation(tag, LAYOUT)) { + // TODO: figure out if that's true + // we are not starting the animation here because any update will come + // from the UPDATE mutation + // layout_.push_back(node); + filteredMutations.push_back(mutation); + // node->previous = node->current; + // node->current = mutation.newChildShadowView; + } else if (layoutAnimationsManager_->hasLayoutAnimation( + tag, ENTERING)) { + entering_.push_back(node); + filteredMutations.push_back(mutation); + } else { + filteredMutations.push_back(mutation); + } + break; + } + case ShadowViewMutation::Remove: { + auto &node = lightNodes_[mutation.oldChildShadowView.tag]; + auto &parent = lightNodes_[mutation.parentTag]; + + if (node->intent == TO_DELETE && parent->intent != TO_DELETE) { + exiting_.push_back(node); + if (parent->children[mutation.index]->current.tag == + mutation.oldChildShadowView.tag) { + filteredMutations.push_back(mutation); + } else { + throw "cos jest nie tak z indexami"; + } + parent->children.erase(parent->children.begin() + mutation.index); + } else if (node->intent != TO_DELETE) { + if (parent->children[mutation.index]->current.tag == + mutation.oldChildShadowView.tag) { + filteredMutations.push_back(mutation); + } else { + throw "cos jest nie tak z indexami"; + } + parent->children.erase(parent->children.begin() + mutation.index); + } + break; + } + default: + break; + } + } +} + +// MARK: Shared Element Transitions + LightNode::Unshared LayoutAnimationsProxy::findTopScreen( LightNode::Unshared node) const { LightNode::Unshared result = nullptr; @@ -172,168 +321,6 @@ void LayoutAnimationsProxy::findSharedElementsOnScreen( } } -std::vector -LayoutAnimationsProxy::getAbsolutePositionsForRootPathView( - const LightNode::Unshared &node) const { - std::vector viewsAbsolutePositions; - auto currentNode = node; - while (currentNode) { - react::Point viewPosition; - if (!strcmp(currentNode->current.componentName, "ScrollView")) { - auto state = std::static_pointer_cast( - currentNode->current.state); - auto data = state->getData(); - viewPosition -= data.contentOffset; - } - if (!strcmp(currentNode->current.componentName, "RNSScreen") && - currentNode->children.size() >= 2) { - const auto &parent = currentNode->parent.lock(); - if (parent) { - float headerHeight = parent->current.layoutMetrics.frame.size.height - - currentNode->current.layoutMetrics.frame.size.height; - viewPosition.y += headerHeight; - } - } - viewPosition += currentNode->current.layoutMetrics.frame.origin; - viewsAbsolutePositions.emplace_back(viewPosition); - currentNode = currentNode->parent.lock(); - } - for (long int i = viewsAbsolutePositions.size() - 2; i >= 0; --i) { - viewsAbsolutePositions[i] += viewsAbsolutePositions[i + 1]; - } - return viewsAbsolutePositions; -} - -void LayoutAnimationsProxy::parseParentTransforms( - const LightNode::Unshared &node, - const std::vector &absolutePositions) const { - std::vector> transforms; - auto currentNode = node; - while (currentNode) { - const auto &props = - static_cast(*currentNode->current.props); - auto origin = props.transformOrigin; - const auto &viewSize = currentNode->current.layoutMetrics.frame.size; - if (origin.xy[0].unit == facebook::react::UnitType::Percent) { - origin.xy[0] = { - static_cast(viewSize.width * origin.xy[0].value / 100), - UnitType::Point}; - } else if (origin.xy[0].unit == facebook::react::UnitType::Undefined) { - origin.xy[0] = { - static_cast(viewSize.width * 0.5), UnitType::Point}; - } - if (origin.xy[1].unit == facebook::react::UnitType::Percent) { - origin.xy[1] = { - static_cast(viewSize.height * origin.xy[1].value / 100), - UnitType::Point}; - } else if (origin.xy[1].unit == facebook::react::UnitType::Undefined) { - origin.xy[1] = { - static_cast(viewSize.height * 0.5), UnitType::Point}; - } - transforms.emplace_back(props.transform, origin); - currentNode = currentNode->parent.lock(); - } - - const auto &targetViewPosition = absolutePositions[0]; - Transform combinedMatrix; - bool parentHasTransform = false; - for (long int i = transforms.size() - 1; i >= 0; --i) { - auto &[transform, transformOrigin] = transforms[i]; - if (transform.operations.empty()) { - continue; - } else if (i > 0) { - parentHasTransform = true; - } - if (i == 0 && !parentHasTransform) { - // If only target view has transform, lets skip it, to matrix - // decomposition in JS - break; - } - transformOrigin.xy[0].value -= - targetViewPosition.x - absolutePositions[i].x; - transformOrigin.xy[1].value -= - targetViewPosition.y - absolutePositions[i].y; - combinedMatrix = - combinedMatrix * - resolveTransform( - node->current.layoutMetrics, transform, transformOrigin); - combinedMatrix.operations.clear(); - } - if (parentHasTransform) { - transformForNode_[node->current.tag] = Transform::FromTransformOperation( - react::TransformOperation(TransformOperationType::Arbitrary), - {}, - combinedMatrix); - } -} - -// The methods resolveTransform and getTranslateForTransformOrigin are sourced -// from: -// https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.cpp#L548 -// We need a copy of these methods to modify the `resolveTransform` method -// to accept the transform origin as a parameter instead of as a class field. -react::Transform LayoutAnimationsProxy::resolveTransform( - const LayoutMetrics &layoutMetrics, - const Transform &transform, - const TransformOrigin &transformOrigin) const { - const auto &frameSize = layoutMetrics.frame.size; - auto transformMatrix = Transform{}; - if (frameSize.width == 0 && frameSize.height == 0) { - return transformMatrix; - } - - if (transform.operations.size() == 1 && - transform.operations[0].type == - facebook::react::TransformOperationType::Arbitrary) { - transformMatrix = transform; - } else { - for (const auto &operation : transform.operations) { - transformMatrix = transformMatrix * - Transform::FromTransformOperation( - operation, layoutMetrics.frame.size, transform); - } - } - - if (transformOrigin.isSet()) { - std::array translateOffsets = getTranslateForTransformOrigin( - frameSize.width, frameSize.height, transformOrigin); - transformMatrix = - Transform::Translate( - translateOffsets[0], translateOffsets[1], translateOffsets[2]) * - transformMatrix * - Transform::Translate( - -translateOffsets[0], -translateOffsets[1], -translateOffsets[2]); - } - - return transformMatrix; -} - -std::array LayoutAnimationsProxy::getTranslateForTransformOrigin( - float viewWidth, - float viewHeight, - const TransformOrigin &transformOrigin) const { - float viewCenterX = viewWidth / 2; - float viewCenterY = viewHeight / 2; - - std::array origin = {viewCenterX, viewCenterY, transformOrigin.z}; - - for (size_t i = 0; i < transformOrigin.xy.size(); ++i) { - const auto ¤tOrigin = transformOrigin.xy[i]; - if (currentOrigin.unit == UnitType::Point) { - origin[i] = currentOrigin.value; - } else if (currentOrigin.unit == UnitType::Percent) { - origin[i] = - ((i == 0) ? viewWidth : viewHeight) * currentOrigin.value / 100.0f; - } - } - - float newTranslateX = -viewCenterX + origin[0]; - float newTranslateY = -viewCenterY + origin[1]; - float newTranslateZ = origin[2]; - - return {newTranslateX, newTranslateY, newTranslateZ}; -} - void LayoutAnimationsProxy::handleProgressTransition( ShadowViewMutationList &filteredMutations, const ShadowViewMutationList &mutations, @@ -431,186 +418,47 @@ void LayoutAnimationsProxy::handleProgressTransition( afterProps->borderRadii.all.value_or(ValueUnit(0, UnitType::Point)) .value; - auto d = folly::dynamic::object( - "borderRadius", - beforeRadius + transitionProgress_ * (afterRadius - beforeRadius)); - -#ifdef RN_SERIALIZABLE_STATE - // auto rawProps = RawProps(folly::dynamic::merge( - // layoutAnimation.finalView->props->rawProps, d)); - Props::Shared newProps = nullptr; -#else - auto rawProps = RawProps(std::move(d)); - - auto newProps = - getComponentDescriptorForShadowView(layoutAnimation.finalView) - .cloneProps( - propsParserContext, - layoutAnimation.finalView.props, - std::move(rawProps)); -#endif - - updateMap.insert_or_assign( - tag, UpdateValues{newProps, {x, y, width, height}}); - } - } - - if (transitionState_ == START) { - transitionState_ = ACTIVE; - } else if (transitionState_ == END || transitionState_ == CANCELLED) { - for (auto tag : activeTransitions_) { - sharedContainersToRemove_.push_back(tag); - tagsToRestore_.push_back(restoreMap_[tag][1]); - if (transitionState_ == CANCELLED) { - tagsToRestore_.push_back(restoreMap_[tag][0]); - } - } - if (transitionState_ == END) { - topScreen[surfaceId] = lightNodes_[transitionTag_]; - synchronized_ = false; - } - sharedTransitionManager_->groups_.clear(); - activeTransitions_.clear(); - transitionState_ = NONE; - } - } -} - -void LayoutAnimationsProxy::updateLightTree( - const PropsParserContext &propsParserContext, - const ShadowViewMutationList &mutations, - ShadowViewMutationList &filteredMutations) const { - ReanimatedSystraceSection s("updateLightTree"); - std::unordered_set moved, deleted; - for (auto it = mutations.rbegin(); it != mutations.rend(); it++) { - const auto &mutation = *it; - switch (mutation.type) { - case ShadowViewMutation::Delete: { - deleted.insert(mutation.oldChildShadowView.tag); - break; - } - case ShadowViewMutation::Insert: { - moved.insert(mutation.newChildShadowView.tag); - break; - } - case ShadowViewMutation::Remove: { - const auto tag = mutation.oldChildShadowView.tag; - if (deleted.contains(tag)) { - lightNodes_[tag]->intent = TO_DELETE; - } else if (moved.contains(tag)) { - lightNodes_[tag]->intent = TO_MOVE; - } - break; - } - default: { - } - } - } - - for (auto &mutation : mutations) { - maybeUpdateWindowDimensions(mutation); - switch (mutation.type) { - case ShadowViewMutation::Update: { - auto &node = lightNodes_[mutation.newChildShadowView.tag]; - if (!node) { - node = std::make_shared(); - } - node->previous = mutation.oldChildShadowView; -#ifdef ANDROID - if (node->current.props) { - // on android rawProps are used to store the diffed props - // so we need to merge them - // this should soon be replaced in RN with Props 2.0 (the diffing will - // be done at the end of the pipeline) - auto ¤tRawProps = node->current.props->rawProps; - auto mergedRawProps = folly::dynamic::merge( - currentRawProps, mutation.newChildShadowView.props->rawProps); - node->current = mutation.newChildShadowView; - node->current.props = - getComponentDescriptorForShadowView(node->current) - .cloneProps( - propsParserContext, - mutation.newChildShadowView.props, - RawProps(mergedRawProps)); - } else { - node->current = mutation.newChildShadowView; - } + auto d = folly::dynamic::object( + "borderRadius", + beforeRadius + transitionProgress_ * (afterRadius - beforeRadius)); + +#ifdef RN_SERIALIZABLE_STATE + // auto rawProps = RawProps(folly::dynamic::merge( + // layoutAnimation.finalView->props->rawProps, d)); + Props::Shared newProps = nullptr; #else - node->current = mutation.newChildShadowView; + auto rawProps = RawProps(std::move(d)); + + auto newProps = + getComponentDescriptorForShadowView(layoutAnimation.finalView) + .cloneProps( + propsParserContext, + layoutAnimation.finalView.props, + std::move(rawProps)); #endif - auto tag = mutation.newChildShadowView.tag; - if (layoutAnimationsManager_->hasLayoutAnimation(tag, LAYOUT)) { - layout_.push_back(node); - } else { - filteredMutations.push_back(mutation); - } - break; - } - case ShadowViewMutation::Create: { - auto &node = lightNodes_[mutation.newChildShadowView.tag]; - node = std::make_shared(); - node->current = mutation.newChildShadowView; - filteredMutations.push_back(mutation); - break; - } - case ShadowViewMutation::Delete: { - // lightNodes_.erase(mutation.oldChildShadowView.tag); - break; - } - case ShadowViewMutation::Insert: { - transferConfigFromNativeID( - mutation.newChildShadowView.props->nativeId, - mutation.newChildShadowView.tag); - auto &node = lightNodes_[mutation.newChildShadowView.tag]; - auto &parent = lightNodes_[mutation.parentTag]; - parent->children.insert( - parent->children.begin() + mutation.index, node); - node->parent = parent; - const auto tag = mutation.newChildShadowView.tag; - if (node->intent == TO_MOVE && - layoutAnimationsManager_->hasLayoutAnimation(tag, LAYOUT)) { - // TODO: figure out if that's true - // we are not starting the animation here because any update will come - // from the UPDATE mutation - // layout_.push_back(node); - filteredMutations.push_back(mutation); - // node->previous = node->current; - // node->current = mutation.newChildShadowView; - } else if (layoutAnimationsManager_->hasLayoutAnimation( - tag, ENTERING)) { - entering_.push_back(node); - filteredMutations.push_back(mutation); - } else { - filteredMutations.push_back(mutation); - } - break; + + updateMap.insert_or_assign( + tag, UpdateValues{newProps, {x, y, width, height}}); } - case ShadowViewMutation::Remove: { - auto &node = lightNodes_[mutation.oldChildShadowView.tag]; - auto &parent = lightNodes_[mutation.parentTag]; + } - if (node->intent == TO_DELETE && parent->intent != TO_DELETE) { - exiting_.push_back(node); - if (parent->children[mutation.index]->current.tag == - mutation.oldChildShadowView.tag) { - filteredMutations.push_back(mutation); - } else { - throw "cos jest nie tak z indexami"; - } - parent->children.erase(parent->children.begin() + mutation.index); - } else if (node->intent != TO_DELETE) { - if (parent->children[mutation.index]->current.tag == - mutation.oldChildShadowView.tag) { - filteredMutations.push_back(mutation); - } else { - throw "cos jest nie tak z indexami"; - } - parent->children.erase(parent->children.begin() + mutation.index); + if (transitionState_ == START) { + transitionState_ = ACTIVE; + } else if (transitionState_ == END || transitionState_ == CANCELLED) { + for (auto tag : activeTransitions_) { + sharedContainersToRemove_.push_back(tag); + tagsToRestore_.push_back(restoreMap_[tag][1]); + if (transitionState_ == CANCELLED) { + tagsToRestore_.push_back(restoreMap_[tag][0]); } - break; } - default: - break; + if (transitionState_ == END) { + topScreen[surfaceId] = lightNodes_[transitionTag_]; + synchronized_ = false; + } + sharedTransitionManager_->groups_.clear(); + activeTransitions_.clear(); + transitionState_ = NONE; } } } @@ -773,6 +621,53 @@ void LayoutAnimationsProxy::hideTransitioningViews( } } +std::optional LayoutAnimationsProxy::onTransitionProgress( + int tag, + double progress, + bool isClosing, + bool isGoingForward, + bool isSwiping) { + auto lock = std::unique_lock(mutex); + transitionUpdated_ = true; + bool isAndroid; +#ifdef ANDROID + isAndroid = true; +#else + isAndroid = false; +#endif + // TODO: this new approach causes all back transitions to be progress + // transitions + if (!isClosing && !isGoingForward && !isAndroid) { + transitionProgress_ = progress; + if (transitionState_ == NONE && progress < 1) { + transitionState_ = START; + transitionTag_ = tag; + } + // else if (transitionState_ == ACTIVE && progress < eps){ + // transitionState_ = CANCELLED; + // } + else if (transitionState_ == ACTIVE && progress == 1) { + transitionState_ = END; + } + // TODO: unfix + return 1; + } + return {}; +} + +std::optional LayoutAnimationsProxy::onGestureCancel() { + auto lock = std::unique_lock(mutex); + if (transitionState_) { + transitionState_ = CANCELLED; + transitionUpdated_ = true; + // TODO: unfix + return 1; + } + return {}; +} + +// MARK: Layout Animation Updates + std::optional LayoutAnimationsProxy::progressLayoutAnimation( int tag, const jsi::Object &newStyle) { @@ -856,51 +751,6 @@ std::optional LayoutAnimationsProxy::endLayoutAnimation( return surfaceId; } -std::optional LayoutAnimationsProxy::onTransitionProgress( - int tag, - double progress, - bool isClosing, - bool isGoingForward, - bool isSwiping) { - auto lock = std::unique_lock(mutex); - transitionUpdated_ = true; - bool isAndroid; -#ifdef ANDROID - isAndroid = true; -#else - isAndroid = false; -#endif - // TODO: this new approach causes all back transitions to be progress - // transitions - if (!isClosing && !isGoingForward && !isAndroid) { - transitionProgress_ = progress; - if (transitionState_ == NONE && progress < 1) { - transitionState_ = START; - transitionTag_ = tag; - } - // else if (transitionState_ == ACTIVE && progress < eps){ - // transitionState_ = CANCELLED; - // } - else if (transitionState_ == ACTIVE && progress == 1) { - transitionState_ = END; - } - // TODO: unfix - return 1; - } - return {}; -} - -std::optional LayoutAnimationsProxy::onGestureCancel() { - auto lock = std::unique_lock(mutex); - if (transitionState_) { - transitionState_ = CANCELLED; - transitionUpdated_ = true; - // TODO: unfix - return 1; - } - return {}; -} - void LayoutAnimationsProxy::handleRemovals( ShadowViewMutationList &filteredMutations, std::vector> &roots) const { @@ -1102,22 +952,104 @@ bool LayoutAnimationsProxy::startAnimationsRecursively( node->removeChild(subNode); } - bool wantAnimateExit = hasExitAnimation || hasAnimatedChildren; + bool wantAnimateExit = hasExitAnimation || hasAnimatedChildren; + + if (hasExitAnimation) { + node->state = ANIMATING; + startExitingAnimation(node); + } else { + // layoutAnimationsManager_->clearLayoutAnimationConfig(node->tag); + } + + return wantAnimateExit; +} + +void LayoutAnimationsProxy::updateOngoingAnimationTarget( + const int tag, + const ShadowViewMutation &mutation) const { + layoutAnimations_[tag].finalView = 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; +} + +std::shared_ptr LayoutAnimationsProxy::cloneViewWithOpacity( + facebook::react::ShadowViewMutation &mutation, + const PropsParserContext &propsParserContext) const { + auto newView = std::make_shared(mutation.newChildShadowView); + const auto &props = static_cast(*newView.get()->props); + folly::dynamic opacity = folly::dynamic::object("opacity", props.opacity); + auto newProps = getComponentDescriptorForShadowView(*newView).cloneProps( + propsParserContext, newView->props, RawProps(opacity)); + newView->props = newProps; + return newView; +} - if (hasExitAnimation) { - node->state = ANIMATING; - startExitingAnimation(node); - } else { - // layoutAnimationsManager_->clearLayoutAnimationConfig(node->tag); +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(); } - - return wantAnimateExit; } -bool LayoutAnimationsProxy::shouldOverridePullTransaction() const { - return true; +void LayoutAnimationsProxy::maybeUpdateWindowDimensions( + const facebook::react::ShadowViewMutation &mutation) const { + if (mutation.type == ShadowViewMutation::Update && + !std::strcmp( + mutation.oldChildShadowView.componentName, RootComponentName)) { + surfaceManager.updateWindow( + mutation.newChildShadowView.tag, + mutation.newChildShadowView.layoutMetrics.frame.size.width, + mutation.newChildShadowView.layoutMetrics.frame.size.height); + } } +// MARK: Start Animation + ShadowView LayoutAnimationsProxy::createLayoutAnimation(ShadowView &before, const ShadowView& after, const Tag parentTag) const { auto count = 1; const auto tag = after.tag; @@ -1359,88 +1291,168 @@ void LayoutAnimationsProxy::startProgressTransition( }); } -void LayoutAnimationsProxy::updateOngoingAnimationTarget( - const int tag, - const ShadowViewMutation &mutation) const { - layoutAnimations_[tag].finalView = mutation.newChildShadowView; -} +// MARK: Position Calculation -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; +std::vector +LayoutAnimationsProxy::getAbsolutePositionsForRootPathView( + const LightNode::Unshared &node) const { + std::vector viewsAbsolutePositions; + auto currentNode = node; + while (currentNode) { + react::Point viewPosition; + if (!strcmp(currentNode->current.componentName, "ScrollView")) { + auto state = std::static_pointer_cast( + currentNode->current.state); + auto data = state->getData(); + viewPosition -= data.contentOffset; } - - auto &uiRuntime = strongThis->uiRuntime_; - strongThis->layoutAnimationsManager_->cancelLayoutAnimation(uiRuntime, tag); - }); + if (!strcmp(currentNode->current.componentName, "RNSScreen") && + currentNode->children.size() >= 2) { + const auto &parent = currentNode->parent.lock(); + if (parent) { + float headerHeight = parent->current.layoutMetrics.frame.size.height - + currentNode->current.layoutMetrics.frame.size.height; + viewPosition.y += headerHeight; + } + } + viewPosition += currentNode->current.layoutMetrics.frame.origin; + viewsAbsolutePositions.emplace_back(viewPosition); + currentNode = currentNode->parent.lock(); + } + for (long int i = viewsAbsolutePositions.size() - 2; i >= 0; --i) { + viewsAbsolutePositions[i] += viewsAbsolutePositions[i + 1]; + } + return viewsAbsolutePositions; } -void LayoutAnimationsProxy::transferConfigFromNativeID( - const std::string nativeIdString, - const int tag) const { - if (nativeIdString.empty()) { - return; +void LayoutAnimationsProxy::parseParentTransforms( + const LightNode::Unshared &node, + const std::vector &absolutePositions) const { + std::vector> transforms; + auto currentNode = node; + while (currentNode) { + const auto &props = + static_cast(*currentNode->current.props); + auto origin = props.transformOrigin; + const auto &viewSize = currentNode->current.layoutMetrics.frame.size; + if (origin.xy[0].unit == facebook::react::UnitType::Percent) { + origin.xy[0] = { + static_cast(viewSize.width * origin.xy[0].value / 100), + UnitType::Point}; + } else if (origin.xy[0].unit == facebook::react::UnitType::Undefined) { + origin.xy[0] = { + static_cast(viewSize.width * 0.5), UnitType::Point}; + } + if (origin.xy[1].unit == facebook::react::UnitType::Percent) { + origin.xy[1] = { + static_cast(viewSize.height * origin.xy[1].value / 100), + UnitType::Point}; + } else if (origin.xy[1].unit == facebook::react::UnitType::Undefined) { + origin.xy[1] = { + static_cast(viewSize.height * 0.5), UnitType::Point}; + } + transforms.emplace_back(props.transform, origin); + currentNode = currentNode->parent.lock(); } - try { - auto nativeId = stoi(nativeIdString); - layoutAnimationsManager_->transferConfigFromNativeID(nativeId, tag); - } catch (std::invalid_argument) { - } catch (std::out_of_range) { + + const auto &targetViewPosition = absolutePositions[0]; + Transform combinedMatrix; + bool parentHasTransform = false; + for (long int i = transforms.size() - 1; i >= 0; --i) { + auto &[transform, transformOrigin] = transforms[i]; + if (transform.operations.empty()) { + continue; + } else if (i > 0) { + parentHasTransform = true; + } + if (i == 0 && !parentHasTransform) { + // If only target view has transform, lets skip it, to matrix + // decomposition in JS + break; + } + transformOrigin.xy[0].value -= + targetViewPosition.x - absolutePositions[i].x; + transformOrigin.xy[1].value -= + targetViewPosition.y - absolutePositions[i].y; + combinedMatrix = + combinedMatrix * + resolveTransform( + node->current.layoutMetrics, transform, transformOrigin); + combinedMatrix.operations.clear(); + } + if (parentHasTransform) { + transformForNode_[node->current.tag] = Transform::FromTransformOperation( + react::TransformOperation(TransformOperationType::Arbitrary), + {}, + combinedMatrix); } } -// 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; -} +// The methods resolveTransform and getTranslateForTransformOrigin are sourced +// from: +// https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.cpp#L548 +// We need a copy of these methods to modify the `resolveTransform` method +// to accept the transform origin as a parameter instead of as a class field. +react::Transform LayoutAnimationsProxy::resolveTransform( + const LayoutMetrics &layoutMetrics, + const Transform &transform, + const TransformOrigin &transformOrigin) const { + const auto &frameSize = layoutMetrics.frame.size; + auto transformMatrix = Transform{}; + if (frameSize.width == 0 && frameSize.height == 0) { + return transformMatrix; + } -std::shared_ptr LayoutAnimationsProxy::cloneViewWithOpacity( - facebook::react::ShadowViewMutation &mutation, - const PropsParserContext &propsParserContext) const { - auto newView = std::make_shared(mutation.newChildShadowView); - const auto &props = static_cast(*newView.get()->props); - folly::dynamic opacity = folly::dynamic::object("opacity", props.opacity); - auto newProps = getComponentDescriptorForShadowView(*newView).cloneProps( - propsParserContext, newView->props, RawProps(opacity)); - newView->props = newProps; - return newView; -} + if (transform.operations.size() == 1 && + transform.operations[0].type == + facebook::react::TransformOperationType::Arbitrary) { + transformMatrix = transform; + } else { + for (const auto &operation : transform.operations) { + transformMatrix = transformMatrix * + Transform::FromTransformOperation( + operation, layoutMetrics.frame.size, transform); + } + } -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(); + if (transformOrigin.isSet()) { + std::array translateOffsets = getTranslateForTransformOrigin( + frameSize.width, frameSize.height, transformOrigin); + transformMatrix = + Transform::Translate( + translateOffsets[0], translateOffsets[1], translateOffsets[2]) * + transformMatrix * + Transform::Translate( + -translateOffsets[0], -translateOffsets[1], -translateOffsets[2]); } + + return transformMatrix; } -void LayoutAnimationsProxy::maybeUpdateWindowDimensions( - const facebook::react::ShadowViewMutation &mutation) const { - if (mutation.type == ShadowViewMutation::Update && - !std::strcmp( - mutation.oldChildShadowView.componentName, RootComponentName)) { - surfaceManager.updateWindow( - mutation.newChildShadowView.tag, - mutation.newChildShadowView.layoutMetrics.frame.size.width, - mutation.newChildShadowView.layoutMetrics.frame.size.height); +std::array LayoutAnimationsProxy::getTranslateForTransformOrigin( + float viewWidth, + float viewHeight, + const TransformOrigin &transformOrigin) const { + float viewCenterX = viewWidth / 2; + float viewCenterY = viewHeight / 2; + + std::array origin = {viewCenterX, viewCenterY, transformOrigin.z}; + + for (size_t i = 0; i < transformOrigin.xy.size(); ++i) { + const auto ¤tOrigin = transformOrigin.xy[i]; + if (currentOrigin.unit == UnitType::Point) { + origin[i] = currentOrigin.value; + } else if (currentOrigin.unit == UnitType::Percent) { + origin[i] = + ((i == 0) ? viewWidth : viewHeight) * currentOrigin.value / 100.0f; + } } + + float newTranslateX = -viewCenterX + origin[0]; + float newTranslateY = -viewCenterY + origin[1]; + float newTranslateZ = origin[2]; + + return {newTranslateX, newTranslateY, newTranslateZ}; } } // namespace reanimated From 6bb2426f3199365167e533f2f0c70132ff40f64b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Mon, 29 Sep 2025 15:46:14 +0200 Subject: [PATCH 51/90] add throw error --- .../cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index d038bcafed04..ad25e5fdc759 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -239,7 +239,7 @@ void LayoutAnimationsProxy::updateLightTree( mutation.oldChildShadowView.tag) { filteredMutations.push_back(mutation); } else { - throw "cos jest nie tak z indexami"; + throw std::runtime_error("cos jest nie tak z indexami"); } parent->children.erase(parent->children.begin() + mutation.index); } else if (node->intent != TO_DELETE) { @@ -247,7 +247,7 @@ void LayoutAnimationsProxy::updateLightTree( mutation.oldChildShadowView.tag) { filteredMutations.push_back(mutation); } else { - throw "cos jest nie tak z indexami"; + throw std::runtime_error("cos jest nie tak z indexami"); } parent->children.erase(parent->children.begin() + mutation.index); } From a233a9917df56c203fe7f588d6a918556870d489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Mon, 29 Sep 2025 15:48:49 +0200 Subject: [PATCH 52/90] cleanup 2 --- .../reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index ad25e5fdc759..ecf855af953b 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -296,19 +296,20 @@ LightNode::Unshared LayoutAnimationsProxy::findTopScreen( return result; } -void LayoutAnimationsProxy::findSharedElementsOnScreen( - const LightNode::Unshared &node, - int index) const { +void LayoutAnimationsProxy::findSharedElementsOnScreen(const LightNode::Unshared &node, int index) const { if (sharedTransitionManager_->tagToName_.contains(node->current.tag)) { + ShadowView copy = node->current; std::vector absolutePositions; absolutePositions = getAbsolutePositionsForRootPathView(node); copy.layoutMetrics.frame.origin = absolutePositions[0]; + auto sharedTag = sharedTransitionManager_->tagToName_[node->current.tag]; auto &transition = transitionMap_[sharedTag]; transition.snapshot[index] = copy; transition.parentTag[index] = node->parent.lock()->current.tag; parseParentTransforms(node, absolutePositions); + if (transition.parentTag[0] && transition.parentTag[1]) { transitions_.push_back({sharedTag, transition}); } else if (transition.parentTag[1]) { From c584dc51a1fad3c94c070ee01612ce7e39f231fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Mon, 29 Sep 2025 16:13:31 +0200 Subject: [PATCH 53/90] cleanup 3 --- .../LayoutAnimationsProxy.cpp | 78 +++++++------------ .../LayoutAnimations/LayoutAnimationsProxy.h | 8 +- 2 files changed, 33 insertions(+), 53 deletions(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index ecf855af953b..c571b2c55f2e 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -344,30 +344,23 @@ void LayoutAnimationsProxy::handleProgressTransition( if (beforeTopScreen->current.tag != afterTopScreen->current.tag) { for (auto &[sharedTag, transition] : transitions_) { const auto &[before, after] = transition.snapshot; - const auto &[beforeParentTag, afterParentTag] = - transition.parentTag; + const auto &[beforeParentTag, afterParentTag] = transition.parentTag; auto &root = lightNodes_[surfaceId]; ShadowView s = before; s.tag = myTag; filteredMutations.push_back(ShadowViewMutation::CreateMutation(s)); - filteredMutations.push_back(ShadowViewMutation::InsertMutation( - surfaceId, s, root->children.size())); - filteredMutations.push_back(ShadowViewMutation::UpdateMutation( - after, after, afterParentTag)); + filteredMutations.push_back(ShadowViewMutation::InsertMutation(surfaceId, s, root->children.size())); + filteredMutations.push_back(ShadowViewMutation::UpdateMutation(after, after, afterParentTag)); auto p = lightNodes_[before.tag]->parent.lock(); - auto m1 = - ShadowViewMutation::InsertMutation(p->current.tag, before, 8); filteredMutations.push_back(ShadowViewMutation::UpdateMutation( before, - *cloneViewWithoutOpacity(m1, propsParserContext), + cloneViewWithoutOpacity(before, propsParserContext), p->current.tag)); auto m = ShadowViewMutation::UpdateMutation( - after, after, afterParentTag); - m = ShadowViewMutation::UpdateMutation( after, - *cloneViewWithoutOpacity(m, propsParserContext), + cloneViewWithoutOpacity(after, propsParserContext), afterParentTag); filteredMutations.push_back(m); auto node = std::make_shared(); @@ -525,7 +518,7 @@ void LayoutAnimationsProxy::handleSharedTransitionsStart( ShadowViewMutation::UpdateMutation(after, after, afterParentTag); m = ShadowViewMutation::UpdateMutation( after, - *cloneViewWithoutOpacity(m, propsParserContext), + cloneViewWithoutOpacity(after, propsParserContext), afterParentTag); filteredMutations.push_back(m); auto node = std::make_shared(); @@ -581,10 +574,9 @@ void LayoutAnimationsProxy::cleanupSharedTransitions( if (node) { auto view = node->current; auto parentTag = node->parent.lock()->current.tag; - auto m = ShadowViewMutation::UpdateMutation(view, view, parentTag); - m = ShadowViewMutation::UpdateMutation( - *cloneViewWithoutOpacity(m, propsParserContext), - *cloneViewWithOpacity(m, propsParserContext), + auto m = ShadowViewMutation::UpdateMutation( + cloneViewWithoutOpacity(view, propsParserContext), + cloneViewWithOpacity(view, propsParserContext), parentTag); filteredMutations.push_back(m); } @@ -614,10 +606,8 @@ void LayoutAnimationsProxy::hideTransitioningViews( for (auto &[sharedTag, transition] : transitions_) { const auto &shadowView = transition.snapshot[index]; const auto &parentTag = transition.parentTag[index]; - auto m = - ShadowViewMutation::UpdateMutation(shadowView, shadowView, parentTag); - m = ShadowViewMutation::UpdateMutation( - shadowView, *cloneViewWithoutOpacity(m, propsParserContext), parentTag); + auto m = ShadowViewMutation::UpdateMutation( + shadowView, cloneViewWithoutOpacity(shadowView, propsParserContext), parentTag); filteredMutations.push_back(m); } } @@ -854,10 +844,6 @@ void LayoutAnimationsProxy::maybeDropAncestors( std::shared_ptr parent, std::shared_ptr child, ShadowViewMutationList &cleanupMutations) const { - // parent->removeChildFromUnflattenedTree(child); - // if (!parent->isMutationMode()) { - // return; - // } for (auto it = parent->children.begin(); it != parent->children.end(); it++) { if ((*it)->current.tag == child->current.tag) { parent->children.erase(it); @@ -868,10 +854,7 @@ void LayoutAnimationsProxy::maybeDropAncestors( return; } - // auto node = std::static_pointer_cast(parent); - if (parent->children.size() == 0 && parent->state != ANIMATING) { - // nodeForTag_.erase(parent->current.tag); auto pp = parent->parent.lock(); for (int i = 0; i < pp->children.size(); i++) { if (pp->children[i]->current.tag == parent->current.tag) { @@ -942,7 +925,6 @@ bool LayoutAnimationsProxy::startAnimationsRecursively( node->current.tag, subNode->current, index)); toBeRemoved.push_back(subNode); subNode->state = DELETED; - // nodeForTag_.erase(subNode->tag); mutations.push_back(ShadowViewMutation::DeleteMutation(subNode->current)); } else { subNode->state = WAITING; @@ -1004,26 +986,26 @@ void LayoutAnimationsProxy::transferConfigFromNativeID( // 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, +ShadowView LayoutAnimationsProxy::cloneViewWithoutOpacity( + const ShadowView &shadowView, const PropsParserContext &propsParserContext) const { - auto newView = std::make_shared(mutation.newChildShadowView); + auto newView = shadowView; folly::dynamic opacity = folly::dynamic::object("opacity", 0); - auto newProps = getComponentDescriptorForShadowView(*newView).cloneProps( - propsParserContext, newView->props, RawProps(opacity)); - newView->props = newProps; + auto newProps = getComponentDescriptorForShadowView(newView).cloneProps( + propsParserContext, newView.props, RawProps(opacity)); + newView.props = newProps; return newView; } -std::shared_ptr LayoutAnimationsProxy::cloneViewWithOpacity( - facebook::react::ShadowViewMutation &mutation, +ShadowView LayoutAnimationsProxy::cloneViewWithOpacity( + const ShadowView &shadowView, const PropsParserContext &propsParserContext) const { - auto newView = std::make_shared(mutation.newChildShadowView); - const auto &props = static_cast(*newView.get()->props); + auto newView = shadowView; + const auto &props = static_cast(*newView.props); folly::dynamic opacity = folly::dynamic::object("opacity", props.opacity); - auto newProps = getComponentDescriptorForShadowView(*newView).cloneProps( - propsParserContext, newView->props, RawProps(opacity)); - newView->props = newProps; + auto newProps = getComponentDescriptorForShadowView(newView).cloneProps( + propsParserContext, newView.props, RawProps(opacity)); + newView.props = newProps; return newView; } @@ -1066,11 +1048,10 @@ ShadowView LayoutAnimationsProxy::createLayoutAnimation(ShadowView &before, cons auto finalView = after; auto currentView = oldView; auto startView = oldView; + + auto la = LayoutAnimation{finalView, currentView, startView, parentTag, {}, count}; - layoutAnimations_.insert_or_assign( - tag, - LayoutAnimation{ - finalView, currentView, startView, parentTag, {}, count}); + layoutAnimations_.insert_or_assign(tag, std::move(la)); return oldView; } @@ -1101,9 +1082,8 @@ void LayoutAnimationsProxy::startEnteringAnimation(const LightNode::Unshared& no { auto &mutex = strongThis->mutex; auto lock = std::unique_lock(mutex); - strongThis->layoutAnimations_.insert_or_assign( - tag, - LayoutAnimation{newChildShadowView, newChildShadowView, newChildShadowView, parentTag, opacity}); + auto la = LayoutAnimation{newChildShadowView, newChildShadowView, newChildShadowView, parentTag, opacity}; + strongThis->layoutAnimations_.insert_or_assign(tag, std::move(la)); window = strongThis->surfaceManager.getWindow(newChildShadowView.surfaceId); } diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h index 7684984077e7..caf4e7a9e215 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h @@ -177,12 +177,12 @@ struct LayoutAnimationsProxy void updateOngoingAnimationTarget( const int tag, const ShadowViewMutation &mutation) const; - std::shared_ptr cloneViewWithoutOpacity( - facebook::react::ShadowViewMutation &mutation, + ShadowView cloneViewWithoutOpacity( + const ShadowView &shadowView, const PropsParserContext &propsParserContext) const; - std::shared_ptr cloneViewWithOpacity( - facebook::react::ShadowViewMutation &mutation, + ShadowView cloneViewWithOpacity( + const ShadowView &shadowView, const PropsParserContext &propsParserContext) const; void maybeRestoreOpacity( LayoutAnimation &layoutAnimation, From 7226bb5e7184161d4fa0be182189f594b4a56ec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Tue, 30 Sep 2025 10:32:05 +0200 Subject: [PATCH 54/90] simplify transforms --- .../LayoutAnimationsProxy.cpp | 139 ++++++++---------- .../LayoutAnimations/LayoutAnimationsProxy.h | 10 +- .../LayoutAnimations/LayoutAnimationsUtils.h | 8 +- 3 files changed, 72 insertions(+), 85 deletions(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index c571b2c55f2e..46915f8bfda0 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -53,7 +53,7 @@ std::optional LayoutAnimationsProxy::pullTransaction( auto root = lightNodes_[surfaceId]; auto beforeTopScreen = topScreen[surfaceId]; if (beforeTopScreen) { - findSharedElementsOnScreen(beforeTopScreen, 0); + findSharedElementsOnScreen(beforeTopScreen, 0, propsParserContext); } updateLightTree(propsParserContext, mutations, filteredMutations); @@ -62,7 +62,7 @@ std::optional LayoutAnimationsProxy::pullTransaction( auto afterTopScreen = findTopScreen(root); topScreen[surfaceId] = afterTopScreen; if (afterTopScreen) { - findSharedElementsOnScreen(afterTopScreen, 1); + findSharedElementsOnScreen(afterTopScreen, 1, propsParserContext); } bool shouldTransitionStart = beforeTopScreen && afterTopScreen && beforeTopScreen != afterTopScreen; @@ -267,8 +267,7 @@ LightNode::Unshared LayoutAnimationsProxy::findTopScreen( if (!node->current.componentName) { return result; } - if (!(strcmp(node->current.componentName, "RNSScreen")) || - !(strcmp(node->current.componentName, "RNSModalScreen"))) { + if (isRNSScreen(node)) { bool isActive = false; #ifdef ANDROID // TODO: this looks like a RNSScreens bug - sometimes there is no active @@ -285,18 +284,17 @@ LightNode::Unshared LayoutAnimationsProxy::findTopScreen( result = node; } } - - for (auto it = node->children.rbegin(); it != node->children.rend(); it++) { - auto t = findTopScreen(*it); - if (t) { - return t; + for (auto child: std::views::reverse(node->children)) { + auto top = findTopScreen(child); + if (top) { + return top; } } return result; } -void LayoutAnimationsProxy::findSharedElementsOnScreen(const LightNode::Unshared &node, int index) const { +void LayoutAnimationsProxy::findSharedElementsOnScreen(const LightNode::Unshared &node, int index, const PropsParserContext &propsParserContext) const { if (sharedTransitionManager_->tagToName_.contains(node->current.tag)) { ShadowView copy = node->current; @@ -306,9 +304,12 @@ void LayoutAnimationsProxy::findSharedElementsOnScreen(const LightNode::Unshared auto sharedTag = sharedTransitionManager_->tagToName_[node->current.tag]; auto &transition = transitionMap_[sharedTag]; + auto transform = parseParentTransforms(node, absolutePositions); + if (transform){ + overrideTransform(copy, *transform, propsParserContext); + } transition.snapshot[index] = copy; transition.parentTag[index] = node->parent.lock()->current.tag; - parseParentTransforms(node, absolutePositions); if (transition.parentTag[0] && transition.parentTag[1]) { transitions_.push_back({sharedTag, transition}); @@ -318,7 +319,7 @@ void LayoutAnimationsProxy::findSharedElementsOnScreen(const LightNode::Unshared } } for (auto &child : node->children) { - findSharedElementsOnScreen(child, index); + findSharedElementsOnScreen(child, index, propsParserContext); } } @@ -338,8 +339,8 @@ void LayoutAnimationsProxy::handleProgressTransition( auto beforeTopScreen = topScreen[surfaceId]; auto afterTopScreen = lightNodes_[transitionTag_]; if (beforeTopScreen && afterTopScreen) { - findSharedElementsOnScreen(beforeTopScreen, 0); - findSharedElementsOnScreen(afterTopScreen, 1); + findSharedElementsOnScreen(beforeTopScreen, 0, propsParserContext); + findSharedElementsOnScreen(afterTopScreen, 1, propsParserContext); if (beforeTopScreen->current.tag != afterTopScreen->current.tag) { for (auto &[sharedTag, transition] : transitions_) { @@ -457,6 +458,27 @@ void LayoutAnimationsProxy::handleProgressTransition( } } +void LayoutAnimationsProxy::overrideTransform(ShadowView &shadowView, const Transform &transform, const PropsParserContext &propsParserContext) const { +#ifdef ANDROID + auto array = folly::dynamic::array(folly::dynamic::object( + "matrix", + transform.operator folly::dynamic())); + folly::dynamic newTransformDynamic = + folly::dynamic::object("transform", array); + auto newRawProps = + folly::dynamic::merge(shadowView.props->rawProps, newTransformDynamic); + auto newProps = getComponentDescriptorForShadowView(shadowView).cloneProps( + propsParserContext, shadowView.props, RawProps(newRawProps)); + auto viewProps = std::const_pointer_cast( + std::static_pointer_cast(newProps)); +#else + auto newProps = getComponentDescriptorForShadowView(shadowView).cloneProps(propsParserContext, shadowView.props, {}); + auto viewProps = std::const_pointer_cast(std::static_pointer_cast(newProps)); + viewProps->transform = transform; +#endif + shadowView.props = newProps; +} + void LayoutAnimationsProxy::handleSharedTransitionsStart( const LightNode::Unshared &afterTopScreen, const LightNode::Unshared &beforeTopScreen, @@ -464,51 +486,25 @@ void LayoutAnimationsProxy::handleSharedTransitionsStart( const ShadowViewMutationList &mutations, const PropsParserContext &propsParserContext, SurfaceId surfaceId) const { - { - ReanimatedSystraceSection s1("moj narzut 2"); + ReanimatedSystraceSection s1("LayoutAnimationsProxy::handleSharedTransitionsStart"); + + if (!beforeTopScreen || !afterTopScreen){ + return; + } - if (beforeTopScreen && afterTopScreen && - beforeTopScreen->current.tag != afterTopScreen->current.tag) { + if (beforeTopScreen != afterTopScreen) { for (auto &[sharedTag, transition] : transitions_) { const auto &[before, after] = transition.snapshot; const auto &[beforeParentTag, afterParentTag] = transition.parentTag; - - auto fakeTag = sharedTransitionManager_->groups_[sharedTag].fakeTag; - auto shouldCreateContainer = - (fakeTag == -1 || !layoutAnimations_.contains(fakeTag)); + auto containerTag = sharedTransitionManager_->groups_[sharedTag].fakeTag; + auto shouldCreateContainer = (containerTag == -1 || !layoutAnimations_.contains(containerTag)); + if (shouldCreateContainer) { auto &root = lightNodes_[surfaceId]; ShadowView s = before; - auto beforeViewProps = std::const_pointer_cast( - std::static_pointer_cast(s.props)); - auto afterViewProps = std::const_pointer_cast( - std::static_pointer_cast(after.props)); s.tag = myTag; - if (transformForNode_.contains(before.tag)) { -#ifdef ANDROID - - auto array = folly::dynamic::array(folly::dynamic::object( - "matrix", - transformForNode_[before.tag].operator folly::dynamic())); - folly::dynamic newTransformDynamic = - folly::dynamic::object("transform", array); - auto newRawProps = - folly::dynamic::merge(s.props->rawProps, newTransformDynamic); - auto newProps = getComponentDescriptorForShadowView(s).cloneProps( - propsParserContext, s.props, RawProps(newRawProps)); - auto viewProps = std::const_pointer_cast( - std::static_pointer_cast(newProps)); -#else - auto newProps = getComponentDescriptorForShadowView(s).cloneProps( - propsParserContext, s.props, {}); - auto viewProps = std::const_pointer_cast( - std::static_pointer_cast(newProps)); - viewProps->transform = transformForNode_[before.tag]; -#endif - s.props = newProps; - } filteredMutations.push_back(ShadowViewMutation::CreateMutation(s)); filteredMutations.push_back(ShadowViewMutation::InsertMutation( surfaceId, s, root->children.size())); @@ -525,27 +521,25 @@ void LayoutAnimationsProxy::handleSharedTransitionsStart( node->current = s; lightNodes_[myTag] = node; root->children.push_back(node); - fakeTag = myTag; + containerTag = myTag; } layoutAnimationsManager_->getConfigsForType( - LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[fakeTag] = + LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[containerTag] = layoutAnimationsManager_->getConfigsForType( LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[before.tag]; ShadowView copy = after; - copy.tag = fakeTag; + copy.tag = containerTag; auto copy2 = before; - copy2.tag = fakeTag; + copy2.tag = containerTag; startSharedTransition( - fakeTag, copy2, copy, surfaceId, before.tag, after.tag); - restoreMap_[fakeTag][1] = after.tag; + containerTag, copy2, copy, surfaceId); + restoreMap_[containerTag][1] = after.tag; if (shouldCreateContainer) { sharedTransitionManager_->groups_[sharedTag].fakeTag = myTag; myTag += 2; } } - } else if ( - mutations.size() && beforeTopScreen && afterTopScreen && - beforeTopScreen->current.tag == afterTopScreen->current.tag) { + } else if (!mutations.empty()) { for (auto &[sharedTag, transition] : transitions_) { const auto &[_, after] = transition.snapshot; @@ -558,11 +552,10 @@ void LayoutAnimationsProxy::handleSharedTransitionsStart( auto &la = layoutAnimations_[fakeTag]; if (la.finalView.layoutMetrics != copy.layoutMetrics) { startSharedTransition( - fakeTag, copy, copy, surfaceId, la.finalView.tag, after.tag); + fakeTag, copy, copy, surfaceId); } } } - } } void LayoutAnimationsProxy::cleanupSharedTransitions( @@ -882,7 +875,7 @@ bool LayoutAnimationsProxy::startAnimationsRecursively( bool shouldAnimate, bool isScreenPop, ShadowViewMutationList &mutations) const { - if (isRNSScreen(node)) { + if (isRNSScreenOrStack(node)) { isScreenPop = true; } @@ -1199,16 +1192,12 @@ void LayoutAnimationsProxy::startSharedTransition( const int tag, const ShadowView &before, const ShadowView &after, - SurfaceId surfaceId, - const int tagBefore, - const int tagAfter) const { + SurfaceId surfaceId) const { uiScheduler_->scheduleOnUI([weakThis = weak_from_this(), before, after, surfaceId, - tag, - tagBefore, - tagAfter]() { + tag]() { auto strongThis = weakThis.lock(); if (!strongThis) { return; @@ -1226,16 +1215,6 @@ void LayoutAnimationsProxy::startSharedTransition( auto &uiRuntime = strongThis->uiRuntime_; auto propsDiffer = PropsDiffer(uiRuntime, oldView, after); - if (tagBefore == -1) { - propsDiffer.overrideTargetTransforms( - strongThis->transformForNode_[tagAfter]); - } else { - propsDiffer.overrideSourceTransforms( - strongThis->transformForNode_[tagBefore]); - propsDiffer.overrideTargetTransforms( - strongThis->transformForNode_[tagAfter]); - } - const auto &propsDiff = propsDiffer.computeDiff(uiRuntime); propsDiff.setProperty(uiRuntime, "windowWidth", window.width); @@ -1306,7 +1285,7 @@ LayoutAnimationsProxy::getAbsolutePositionsForRootPathView( return viewsAbsolutePositions; } -void LayoutAnimationsProxy::parseParentTransforms( +std::optional LayoutAnimationsProxy::parseParentTransforms( const LightNode::Unshared &node, const std::vector &absolutePositions) const { std::vector> transforms; @@ -1362,11 +1341,13 @@ void LayoutAnimationsProxy::parseParentTransforms( combinedMatrix.operations.clear(); } if (parentHasTransform) { - transformForNode_[node->current.tag] = Transform::FromTransformOperation( + return Transform::FromTransformOperation( react::TransformOperation(TransformOperationType::Arbitrary), {}, combinedMatrix); } + + return {}; } // The methods resolveTransform and getTranslateForTransformOrigin are sourced diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h index caf4e7a9e215..93dafce42742 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h @@ -94,9 +94,7 @@ struct LayoutAnimationsProxy const int tag, const ShadowView &before, const ShadowView &after, - SurfaceId surfaceId, - const int tagBefore, - const int tagAfter) const; + SurfaceId surfaceId) const; void startProgressTransition( const int tag, const ShadowView &before, @@ -149,13 +147,15 @@ struct LayoutAnimationsProxy LightNode::Unshared findTopScreen(LightNode::Unshared node) const; - void findSharedElementsOnScreen(const LightNode::Unshared &node, int index) + void findSharedElementsOnScreen(const LightNode::Unshared &node, int index, const PropsParserContext &propsParserContext) const; std::vector getAbsolutePositionsForRootPathView( const LightNode::Unshared &node) const; + + void overrideTransform(ShadowView &shadowView, const Transform &transform, const PropsParserContext &propsParserContext) const; - void parseParentTransforms( + std::optional parseParentTransforms( const LightNode::Unshared &node, const std::vector &absolutePositions) const; react::Transform resolveTransform( diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsUtils.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsUtils.h index 873042f4eb35..3b0295ac16df 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsUtils.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsUtils.h @@ -168,13 +168,19 @@ static inline void updateLayoutMetrics( } } -static inline bool isRNSScreen(std::shared_ptr node) { +static inline bool isRNSScreenOrStack(std::shared_ptr& node) { const auto &componentName = node->current.componentName; return !std::strcmp(componentName, "RNSScreenStack") || !std::strcmp(componentName, "RNSScreen") || !std::strcmp(componentName, "RNSModalScreen"); } +static inline bool isRNSScreen(std::shared_ptr& node) { + const auto &componentName = node->current.componentName; + return !std::strcmp(componentName, "RNSScreen") || + !std::strcmp(componentName, "RNSModalScreen"); +} + static inline bool hasLayoutChanged(const ShadowViewMutation &mutation) { return mutation.oldChildShadowView.layoutMetrics.frame != mutation.newChildShadowView.layoutMetrics.frame; From 6230fdb4cf21b2ab885f63782d26714a05bd9bea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Tue, 30 Sep 2025 10:48:52 +0200 Subject: [PATCH 55/90] cleanup --- .../LayoutAnimationsProxy.cpp | 580 +++++++++--------- .../LayoutAnimations/LayoutAnimationsProxy.h | 27 +- .../LayoutAnimations/LayoutAnimationsUtils.h | 4 +- 3 files changed, 323 insertions(+), 288 deletions(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index 46915f8bfda0..b25da77bbd85 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -64,7 +64,8 @@ std::optional LayoutAnimationsProxy::pullTransaction( if (afterTopScreen) { findSharedElementsOnScreen(afterTopScreen, 1, propsParserContext); } - bool shouldTransitionStart = beforeTopScreen && afterTopScreen && beforeTopScreen != afterTopScreen; + bool shouldTransitionStart = + beforeTopScreen && afterTopScreen && beforeTopScreen != afterTopScreen; if (shouldTransitionStart) { std::vector temp; @@ -284,7 +285,7 @@ LightNode::Unshared LayoutAnimationsProxy::findTopScreen( result = node; } } - for (auto child: std::views::reverse(node->children)) { + for (auto child : std::views::reverse(node->children)) { auto top = findTopScreen(child); if (top) { return top; @@ -294,23 +295,25 @@ LightNode::Unshared LayoutAnimationsProxy::findTopScreen( return result; } -void LayoutAnimationsProxy::findSharedElementsOnScreen(const LightNode::Unshared &node, int index, const PropsParserContext &propsParserContext) const { +void LayoutAnimationsProxy::findSharedElementsOnScreen( + const LightNode::Unshared &node, + int index, + const PropsParserContext &propsParserContext) const { if (sharedTransitionManager_->tagToName_.contains(node->current.tag)) { - ShadowView copy = node->current; std::vector absolutePositions; absolutePositions = getAbsolutePositionsForRootPathView(node); copy.layoutMetrics.frame.origin = absolutePositions[0]; - + auto sharedTag = sharedTransitionManager_->tagToName_[node->current.tag]; auto &transition = transitionMap_[sharedTag]; auto transform = parseParentTransforms(node, absolutePositions); - if (transform){ + if (transform) { overrideTransform(copy, *transform, propsParserContext); } transition.snapshot[index] = copy; transition.parentTag[index] = node->parent.lock()->current.tag; - + if (transition.parentTag[0] && transition.parentTag[1]) { transitions_.push_back({sharedTag, transition}); } else if (transition.parentTag[1]) { @@ -333,147 +336,157 @@ void LayoutAnimationsProxy::handleProgressTransition( } transitionUpdated_ = false; - if (mutations.size() == 0 && transitionState_) { - if (transitionState_ == START) { - auto root = lightNodes_[surfaceId]; - auto beforeTopScreen = topScreen[surfaceId]; - auto afterTopScreen = lightNodes_[transitionTag_]; - if (beforeTopScreen && afterTopScreen) { - findSharedElementsOnScreen(beforeTopScreen, 0, propsParserContext); - findSharedElementsOnScreen(afterTopScreen, 1, propsParserContext); - - if (beforeTopScreen->current.tag != afterTopScreen->current.tag) { - for (auto &[sharedTag, transition] : transitions_) { - const auto &[before, after] = transition.snapshot; - const auto &[beforeParentTag, afterParentTag] = transition.parentTag; - - auto &root = lightNodes_[surfaceId]; - ShadowView s = before; - s.tag = myTag; - filteredMutations.push_back(ShadowViewMutation::CreateMutation(s)); - filteredMutations.push_back(ShadowViewMutation::InsertMutation(surfaceId, s, root->children.size())); - filteredMutations.push_back(ShadowViewMutation::UpdateMutation(after, after, afterParentTag)); - auto p = lightNodes_[before.tag]->parent.lock(); - filteredMutations.push_back(ShadowViewMutation::UpdateMutation( - before, - cloneViewWithoutOpacity(before, propsParserContext), - p->current.tag)); - - auto m = ShadowViewMutation::UpdateMutation( - after, - cloneViewWithoutOpacity(after, propsParserContext), - afterParentTag); - filteredMutations.push_back(m); - auto node = std::make_shared(); - node->current = s; - lightNodes_[myTag] = node; - - root->children.push_back(node); - layoutAnimationsManager_->getConfigsForType( - LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[myTag] = - layoutAnimationsManager_->getConfigsForType( - LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[before.tag]; - ShadowView copy = after; - copy.tag = myTag; - auto copy2 = before; - copy2.tag = myTag; - startProgressTransition(myTag, copy2, copy, surfaceId); - restoreMap_[myTag][0] = before.tag; - restoreMap_[myTag][1] = after.tag; - sharedTransitionManager_->groups_[sharedTag].fakeTag = myTag; - activeTransitions_.insert(myTag); - myTag += 2; - } + if (!mutations.empty() || !transitionState_) { + return; + } + + if (transitionState_ == START) { + auto root = lightNodes_[surfaceId]; + auto beforeTopScreen = topScreen[surfaceId]; + auto afterTopScreen = lightNodes_[transitionTag_]; + if (beforeTopScreen && afterTopScreen) { + findSharedElementsOnScreen(beforeTopScreen, 0, propsParserContext); + findSharedElementsOnScreen(afterTopScreen, 1, propsParserContext); + + if (beforeTopScreen->current.tag != afterTopScreen->current.tag) { + for (auto &[sharedTag, transition] : transitions_) { + const auto &[before, after] = transition.snapshot; + const auto &[beforeParentTag, afterParentTag] = transition.parentTag; + + auto &root = lightNodes_[surfaceId]; + ShadowView s = before; + s.tag = myTag; + filteredMutations.push_back(ShadowViewMutation::CreateMutation(s)); + filteredMutations.push_back(ShadowViewMutation::InsertMutation( + surfaceId, s, root->children.size())); + filteredMutations.push_back( + ShadowViewMutation::UpdateMutation(after, after, afterParentTag)); + auto p = lightNodes_[before.tag]->parent.lock(); + filteredMutations.push_back(ShadowViewMutation::UpdateMutation( + before, + cloneViewWithoutOpacity(before, propsParserContext), + p->current.tag)); + + auto m = ShadowViewMutation::UpdateMutation( + after, + cloneViewWithoutOpacity(after, propsParserContext), + afterParentTag); + filteredMutations.push_back(m); + auto node = std::make_shared(); + node->current = s; + lightNodes_[myTag] = node; + + root->children.push_back(node); + layoutAnimationsManager_->getConfigsForType( + LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[myTag] = + layoutAnimationsManager_->getConfigsForType( + LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[before.tag]; + ShadowView copy = after; + copy.tag = myTag; + auto copy2 = before; + copy2.tag = myTag; + startProgressTransition(myTag, copy2, copy, surfaceId); + restoreMap_[myTag][0] = before.tag; + restoreMap_[myTag][1] = after.tag; + sharedTransitionManager_->groups_[sharedTag].fakeTag = myTag; + activeTransitions_.insert(myTag); + myTag += 2; } } - } else if (transitionState_ == ACTIVE) { - for (auto tag : activeTransitions_) { - auto layoutAnimation = layoutAnimations_[tag]; - auto &updateMap = - surfaceManager.getUpdateMap(layoutAnimation.finalView.surfaceId); - auto before = layoutAnimation.startView.layoutMetrics.frame; - auto after = layoutAnimation.finalView.layoutMetrics.frame; - auto x = before.origin.x + - transitionProgress_ * (after.origin.x - before.origin.x); - auto y = before.origin.y + - transitionProgress_ * (after.origin.y - before.origin.y); - auto width = before.size.width + - transitionProgress_ * (after.size.width - before.size.width); - auto height = before.size.height + - transitionProgress_ * (after.size.height - before.size.height); - - auto beforeProps = std::static_pointer_cast( - layoutAnimation.startView.props); - auto afterProps = std::static_pointer_cast( - layoutAnimation.finalView.props); - auto beforeRadius = - beforeProps->borderRadii.all.value_or(ValueUnit(0, UnitType::Point)) - .value; - auto afterRadius = - afterProps->borderRadii.all.value_or(ValueUnit(0, UnitType::Point)) - .value; - - auto d = folly::dynamic::object( - "borderRadius", - beforeRadius + transitionProgress_ * (afterRadius - beforeRadius)); + } + } else if (transitionState_ == ACTIVE) { + for (auto tag : activeTransitions_) { + auto layoutAnimation = layoutAnimations_[tag]; + auto &updateMap = + surfaceManager.getUpdateMap(layoutAnimation.finalView.surfaceId); + auto before = layoutAnimation.startView.layoutMetrics.frame; + auto after = layoutAnimation.finalView.layoutMetrics.frame; + auto x = before.origin.x + + transitionProgress_ * (after.origin.x - before.origin.x); + auto y = before.origin.y + + transitionProgress_ * (after.origin.y - before.origin.y); + auto width = before.size.width + + transitionProgress_ * (after.size.width - before.size.width); + auto height = before.size.height + + transitionProgress_ * (after.size.height - before.size.height); + + auto beforeProps = std::static_pointer_cast( + layoutAnimation.startView.props); + auto afterProps = std::static_pointer_cast( + layoutAnimation.finalView.props); + auto beforeRadius = + beforeProps->borderRadii.all.value_or(ValueUnit(0, UnitType::Point)) + .value; + auto afterRadius = + afterProps->borderRadii.all.value_or(ValueUnit(0, UnitType::Point)) + .value; + + auto d = folly::dynamic::object( + "borderRadius", + beforeRadius + transitionProgress_ * (afterRadius - beforeRadius)); #ifdef RN_SERIALIZABLE_STATE - // auto rawProps = RawProps(folly::dynamic::merge( - // layoutAnimation.finalView->props->rawProps, d)); - Props::Shared newProps = nullptr; + // auto rawProps = RawProps(folly::dynamic::merge( + // layoutAnimation.finalView->props->rawProps, d)); + Props::Shared newProps = nullptr; #else - auto rawProps = RawProps(std::move(d)); - - auto newProps = - getComponentDescriptorForShadowView(layoutAnimation.finalView) - .cloneProps( - propsParserContext, - layoutAnimation.finalView.props, - std::move(rawProps)); + auto rawProps = RawProps(std::move(d)); + + auto newProps = + getComponentDescriptorForShadowView(layoutAnimation.finalView) + .cloneProps( + propsParserContext, + layoutAnimation.finalView.props, + std::move(rawProps)); #endif - updateMap.insert_or_assign( - tag, UpdateValues{newProps, {x, y, width, height}}); - } + updateMap.insert_or_assign( + tag, UpdateValues{newProps, {x, y, width, height}}); } + } - if (transitionState_ == START) { - transitionState_ = ACTIVE; - } else if (transitionState_ == END || transitionState_ == CANCELLED) { - for (auto tag : activeTransitions_) { - sharedContainersToRemove_.push_back(tag); - tagsToRestore_.push_back(restoreMap_[tag][1]); - if (transitionState_ == CANCELLED) { - tagsToRestore_.push_back(restoreMap_[tag][0]); - } + if (transitionState_ == START) { + transitionState_ = ACTIVE; + } else if (transitionState_ == END || transitionState_ == CANCELLED) { + for (auto tag : activeTransitions_) { + sharedContainersToRemove_.push_back(tag); + tagsToRestore_.push_back(restoreMap_[tag][1]); + if (transitionState_ == CANCELLED) { + tagsToRestore_.push_back(restoreMap_[tag][0]); } - if (transitionState_ == END) { - topScreen[surfaceId] = lightNodes_[transitionTag_]; - synchronized_ = false; - } - sharedTransitionManager_->groups_.clear(); - activeTransitions_.clear(); - transitionState_ = NONE; } + if (transitionState_ == END) { + topScreen[surfaceId] = lightNodes_[transitionTag_]; + synchronized_ = false; + } + sharedTransitionManager_->groups_.clear(); + activeTransitions_.clear(); + transitionState_ = NONE; } } -void LayoutAnimationsProxy::overrideTransform(ShadowView &shadowView, const Transform &transform, const PropsParserContext &propsParserContext) const { +void LayoutAnimationsProxy::overrideTransform( + ShadowView &shadowView, + const Transform &transform, + const PropsParserContext &propsParserContext) const { #ifdef ANDROID - auto array = folly::dynamic::array(folly::dynamic::object( - "matrix", - transform.operator folly::dynamic())); - folly::dynamic newTransformDynamic = - folly::dynamic::object("transform", array); - auto newRawProps = - folly::dynamic::merge(shadowView.props->rawProps, newTransformDynamic); - auto newProps = getComponentDescriptorForShadowView(shadowView).cloneProps( - propsParserContext, shadowView.props, RawProps(newRawProps)); - auto viewProps = std::const_pointer_cast( - std::static_pointer_cast(newProps)); + auto array = folly::dynamic::array( + folly::dynamic::object("matrix", transform.operator folly::dynamic())); + folly::dynamic newTransformDynamic = + folly::dynamic::object("transform", array); + auto newRawProps = + folly::dynamic::merge(shadowView.props->rawProps, newTransformDynamic); + auto newProps = + getComponentDescriptorForShadowView(shadowView) + .cloneProps( + propsParserContext, shadowView.props, RawProps(newRawProps)); + auto viewProps = std::const_pointer_cast( + std::static_pointer_cast(newProps)); #else - auto newProps = getComponentDescriptorForShadowView(shadowView).cloneProps(propsParserContext, shadowView.props, {}); - auto viewProps = std::const_pointer_cast(std::static_pointer_cast(newProps)); + auto newProps = getComponentDescriptorForShadowView(shadowView) + .cloneProps(propsParserContext, shadowView.props, {}); + auto viewProps = std::const_pointer_cast( + std::static_pointer_cast(newProps)); viewProps->transform = transform; #endif shadowView.props = newProps; @@ -486,76 +499,76 @@ void LayoutAnimationsProxy::handleSharedTransitionsStart( const ShadowViewMutationList &mutations, const PropsParserContext &propsParserContext, SurfaceId surfaceId) const { - ReanimatedSystraceSection s1("LayoutAnimationsProxy::handleSharedTransitionsStart"); - - if (!beforeTopScreen || !afterTopScreen){ - return; - } + ReanimatedSystraceSection s1( + "LayoutAnimationsProxy::handleSharedTransitionsStart"); - if (beforeTopScreen != afterTopScreen) { - for (auto &[sharedTag, transition] : transitions_) { - const auto &[before, after] = transition.snapshot; - const auto &[beforeParentTag, afterParentTag] = transition.parentTag; - auto containerTag = sharedTransitionManager_->groups_[sharedTag].fakeTag; - auto shouldCreateContainer = (containerTag == -1 || !layoutAnimations_.contains(containerTag)); - - if (shouldCreateContainer) { - auto &root = lightNodes_[surfaceId]; - ShadowView s = before; + if (!beforeTopScreen || !afterTopScreen) { + return; + } - s.tag = myTag; + if (beforeTopScreen != afterTopScreen) { + for (auto &[sharedTag, transition] : transitions_) { + const auto &[before, after] = transition.snapshot; + const auto &[beforeParentTag, afterParentTag] = transition.parentTag; + auto containerTag = sharedTransitionManager_->groups_[sharedTag].fakeTag; + auto shouldCreateContainer = + (containerTag == -1 || !layoutAnimations_.contains(containerTag)); - filteredMutations.push_back(ShadowViewMutation::CreateMutation(s)); - filteredMutations.push_back(ShadowViewMutation::InsertMutation( - surfaceId, s, root->children.size())); - filteredMutations.push_back( - ShadowViewMutation::UpdateMutation(after, after, afterParentTag)); - auto m = - ShadowViewMutation::UpdateMutation(after, after, afterParentTag); - m = ShadowViewMutation::UpdateMutation( - after, - cloneViewWithoutOpacity(after, propsParserContext), - afterParentTag); - filteredMutations.push_back(m); - auto node = std::make_shared(); - node->current = s; - lightNodes_[myTag] = node; - root->children.push_back(node); - containerTag = myTag; - } - layoutAnimationsManager_->getConfigsForType( - LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[containerTag] = - layoutAnimationsManager_->getConfigsForType( - LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[before.tag]; - ShadowView copy = after; - copy.tag = containerTag; - auto copy2 = before; - copy2.tag = containerTag; - startSharedTransition( - containerTag, copy2, copy, surfaceId); - restoreMap_[containerTag][1] = after.tag; - if (shouldCreateContainer) { - sharedTransitionManager_->groups_[sharedTag].fakeTag = myTag; - myTag += 2; - } + if (shouldCreateContainer) { + auto &root = lightNodes_[surfaceId]; + ShadowView s = before; + + s.tag = myTag; + + filteredMutations.push_back(ShadowViewMutation::CreateMutation(s)); + filteredMutations.push_back(ShadowViewMutation::InsertMutation( + surfaceId, s, root->children.size())); + filteredMutations.push_back( + ShadowViewMutation::UpdateMutation(after, after, afterParentTag)); + auto m = + ShadowViewMutation::UpdateMutation(after, after, afterParentTag); + m = ShadowViewMutation::UpdateMutation( + after, + cloneViewWithoutOpacity(after, propsParserContext), + afterParentTag); + filteredMutations.push_back(m); + auto node = std::make_shared(); + node->current = s; + lightNodes_[myTag] = node; + root->children.push_back(node); + containerTag = myTag; } - } else if (!mutations.empty()) { - for (auto &[sharedTag, transition] : transitions_) { - const auto &[_, after] = transition.snapshot; - - auto copy = after; - auto fakeTag = sharedTransitionManager_->groups_[sharedTag].fakeTag; - copy.tag = fakeTag; - if (!layoutAnimations_.contains(fakeTag)) { - continue; - } - auto &la = layoutAnimations_[fakeTag]; - if (la.finalView.layoutMetrics != copy.layoutMetrics) { - startSharedTransition( - fakeTag, copy, copy, surfaceId); - } + layoutAnimationsManager_->getConfigsForType( + LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[containerTag] = + layoutAnimationsManager_->getConfigsForType( + LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[before.tag]; + ShadowView copy = after; + copy.tag = containerTag; + auto copy2 = before; + copy2.tag = containerTag; + startSharedTransition(containerTag, copy2, copy, surfaceId); + restoreMap_[containerTag][1] = after.tag; + if (shouldCreateContainer) { + sharedTransitionManager_->groups_[sharedTag].fakeTag = myTag; + myTag += 2; + } + } + } else if (!mutations.empty()) { + for (auto &[sharedTag, transition] : transitions_) { + const auto &[_, after] = transition.snapshot; + + auto copy = after; + auto fakeTag = sharedTransitionManager_->groups_[sharedTag].fakeTag; + copy.tag = fakeTag; + if (!layoutAnimations_.contains(fakeTag)) { + continue; + } + auto &la = layoutAnimations_[fakeTag]; + if (la.finalView.layoutMetrics != copy.layoutMetrics) { + startSharedTransition(fakeTag, copy, copy, surfaceId); } } + } } void LayoutAnimationsProxy::cleanupSharedTransitions( @@ -600,7 +613,9 @@ void LayoutAnimationsProxy::hideTransitioningViews( const auto &shadowView = transition.snapshot[index]; const auto &parentTag = transition.parentTag[index]; auto m = ShadowViewMutation::UpdateMutation( - shadowView, cloneViewWithoutOpacity(shadowView, propsParserContext), parentTag); + shadowView, + cloneViewWithoutOpacity(shadowView, propsParserContext), + parentTag); filteredMutations.push_back(m); } } @@ -675,12 +690,11 @@ std::optional LayoutAnimationsProxy::progressLayoutAnimation( 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 newProps = getComponentDescriptorForShadowView(layoutAnimation.finalView) + .cloneProps( + propsParserContext, + layoutAnimation.finalView.props, + std::move(*rawProps)); auto &updateMap = surfaceManager.getUpdateMap(layoutAnimation.finalView.surfaceId); updateMap.insert_or_assign( @@ -1026,12 +1040,15 @@ void LayoutAnimationsProxy::maybeUpdateWindowDimensions( // MARK: Start Animation -ShadowView LayoutAnimationsProxy::createLayoutAnimation(ShadowView &before, const ShadowView& after, const Tag parentTag) const { +ShadowView LayoutAnimationsProxy::createLayoutAnimation( + ShadowView &before, + const ShadowView &after, + const Tag parentTag) const { auto count = 1; const auto tag = after.tag; auto layoutAnimationIt = layoutAnimations_.find(tag); - auto& oldView = before; - + auto &oldView = before; + if (layoutAnimationIt != layoutAnimations_.end()) { auto &layoutAnimation = layoutAnimationIt->second; oldView = layoutAnimation.currentView; @@ -1041,15 +1058,17 @@ ShadowView LayoutAnimationsProxy::createLayoutAnimation(ShadowView &before, cons auto finalView = after; auto currentView = oldView; auto startView = oldView; - - auto la = LayoutAnimation{finalView, currentView, startView, parentTag, {}, count}; + + auto la = + LayoutAnimation{finalView, currentView, startView, parentTag, {}, count}; layoutAnimations_.insert_or_assign(tag, std::move(la)); - + return oldView; } -void LayoutAnimationsProxy::startEnteringAnimation(const LightNode::Unshared& node) const { +void LayoutAnimationsProxy::startEnteringAnimation( + const LightNode::Unshared &node) const { auto newChildShadowView = node->current; auto finalView = newChildShadowView; auto currentView = newChildShadowView; @@ -1075,9 +1094,15 @@ void LayoutAnimationsProxy::startEnteringAnimation(const LightNode::Unshared& no { auto &mutex = strongThis->mutex; auto lock = std::unique_lock(mutex); - auto la = LayoutAnimation{newChildShadowView, newChildShadowView, newChildShadowView, parentTag, opacity}; + auto la = LayoutAnimation{ + newChildShadowView, + newChildShadowView, + newChildShadowView, + parentTag, + opacity}; strongThis->layoutAnimations_.insert_or_assign(tag, std::move(la)); - window = strongThis->surfaceManager.getWindow(newChildShadowView.surfaceId); + window = + strongThis->surfaceManager.getWindow(newChildShadowView.surfaceId); } Snapshot values(newChildShadowView, window); @@ -1096,47 +1121,52 @@ void LayoutAnimationsProxy::startEnteringAnimation(const LightNode::Unshared& no }); } -void LayoutAnimationsProxy::startExitingAnimation(const LightNode::Unshared& node) const { - auto& oldChildShadowView = node->current; +void LayoutAnimationsProxy::startExitingAnimation( + const LightNode::Unshared &node) const { + auto &oldChildShadowView = node->current; const auto surfaceId = oldChildShadowView.surfaceId; const auto tag = oldChildShadowView.tag; const auto parentTag = node->parent.lock()->current.tag; - uiScheduler_->scheduleOnUI( - [weakThis = weak_from_this(), tag, parentTag, oldChildShadowView, surfaceId]() { - auto strongThis = weakThis.lock(); - if (!strongThis) { - return; - } + uiScheduler_->scheduleOnUI([weakThis = weak_from_this(), + tag, + parentTag, + oldChildShadowView, + surfaceId]() { + auto strongThis = weakThis.lock(); + if (!strongThis) { + return; + } - auto oldView = oldChildShadowView; - Rect window{}; - { - auto &mutex = strongThis->mutex; - auto lock = std::unique_lock(mutex); - oldView = strongThis->createLayoutAnimation(oldView, oldView, parentTag); - window = strongThis->surfaceManager.getWindow(surfaceId); - } + auto oldView = oldChildShadowView; + Rect window{}; + { + auto &mutex = strongThis->mutex; + auto lock = std::unique_lock(mutex); + oldView = strongThis->createLayoutAnimation(oldView, oldView, parentTag); + window = strongThis->surfaceManager.getWindow(surfaceId); + } - Snapshot values(oldView, window); + 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); - }); + 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 LightNode::Unshared& node) const { +void LayoutAnimationsProxy::startLayoutAnimation( + const LightNode::Unshared &node) const { auto oldChildShadowView = node->previous; auto newChildShadowView = node->current; auto surfaceId = oldChildShadowView.surfaceId; @@ -1159,7 +1189,8 @@ void LayoutAnimationsProxy::startLayoutAnimation(const LightNode::Unshared& node { auto &mutex = strongThis->mutex; auto lock = std::unique_lock(mutex); - oldView = strongThis->createLayoutAnimation(oldView, newChildShadowView, parentTag); + oldView = strongThis->createLayoutAnimation( + oldView, newChildShadowView, parentTag); window = strongThis->surfaceManager.getWindow(surfaceId); } @@ -1193,39 +1224,37 @@ void LayoutAnimationsProxy::startSharedTransition( const ShadowView &before, const ShadowView &after, SurfaceId surfaceId) const { - uiScheduler_->scheduleOnUI([weakThis = weak_from_this(), - before, - after, - surfaceId, - tag]() { - auto strongThis = weakThis.lock(); - if (!strongThis) { - return; - } + uiScheduler_->scheduleOnUI( + [weakThis = weak_from_this(), before, after, surfaceId, tag]() { + auto strongThis = weakThis.lock(); + if (!strongThis) { + return; + } - auto oldView = before; - Rect window{}; - { - auto &mutex = strongThis->mutex; - auto lock = std::unique_lock(mutex); - oldView = strongThis->createLayoutAnimation(oldView, after, surfaceId); - window = strongThis->surfaceManager.getWindow(surfaceId); - } + auto oldView = before; + Rect window{}; + { + auto &mutex = strongThis->mutex; + auto lock = std::unique_lock(mutex); + oldView = + strongThis->createLayoutAnimation(oldView, after, surfaceId); + window = strongThis->surfaceManager.getWindow(surfaceId); + } - auto &uiRuntime = strongThis->uiRuntime_; - auto propsDiffer = PropsDiffer(uiRuntime, oldView, after); + auto &uiRuntime = strongThis->uiRuntime_; + auto propsDiffer = PropsDiffer(uiRuntime, oldView, after); - const auto &propsDiff = propsDiffer.computeDiff(uiRuntime); + const auto &propsDiff = propsDiffer.computeDiff(uiRuntime); - propsDiff.setProperty(uiRuntime, "windowWidth", window.width); - propsDiff.setProperty(uiRuntime, "windowHeight", window.height); + propsDiff.setProperty(uiRuntime, "windowWidth", window.width); + propsDiff.setProperty(uiRuntime, "windowHeight", window.height); - strongThis->layoutAnimationsManager_->startLayoutAnimation( - uiRuntime, - tag, - LayoutAnimationType::SHARED_ELEMENT_TRANSITION, - propsDiff); - }); + strongThis->layoutAnimationsManager_->startLayoutAnimation( + uiRuntime, + tag, + LayoutAnimationType::SHARED_ELEMENT_TRANSITION, + propsDiff); + }); } void LayoutAnimationsProxy::startProgressTransition( @@ -1245,7 +1274,8 @@ void LayoutAnimationsProxy::startProgressTransition( { auto &mutex = strongThis->mutex; auto lock = std::unique_lock(mutex); - oldView = strongThis->createLayoutAnimation(oldView, after, surfaceId); + oldView = + strongThis->createLayoutAnimation(oldView, after, surfaceId); window = strongThis->surfaceManager.getWindow(surfaceId); } }); @@ -1346,7 +1376,7 @@ std::optional LayoutAnimationsProxy::parseParentTransforms( {}, combinedMatrix); } - + return {}; } diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h index 93dafce42742..151926f46f9d 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h @@ -84,12 +84,9 @@ struct LayoutAnimationsProxy lightNodes_[11] = std::make_shared(); } - void startEnteringAnimation(const LightNode::Unshared& node) - const; - void startExitingAnimation(const LightNode::Unshared& node) - const; - void startLayoutAnimation(const LightNode::Unshared& node) - const; + void startEnteringAnimation(const LightNode::Unshared &node) const; + void startExitingAnimation(const LightNode::Unshared &node) const; + void startLayoutAnimation(const LightNode::Unshared &node) const; void startSharedTransition( const int tag, const ShadowView &before, @@ -147,13 +144,18 @@ struct LayoutAnimationsProxy LightNode::Unshared findTopScreen(LightNode::Unshared node) const; - void findSharedElementsOnScreen(const LightNode::Unshared &node, int index, const PropsParserContext &propsParserContext) - const; + void findSharedElementsOnScreen( + const LightNode::Unshared &node, + int index, + const PropsParserContext &propsParserContext) const; std::vector getAbsolutePositionsForRootPathView( const LightNode::Unshared &node) const; - - void overrideTransform(ShadowView &shadowView, const Transform &transform, const PropsParserContext &propsParserContext) const; + + void overrideTransform( + ShadowView &shadowView, + const Transform &transform, + const PropsParserContext &propsParserContext) const; std::optional parseParentTransforms( const LightNode::Unshared &node, @@ -189,7 +191,10 @@ struct LayoutAnimationsProxy const jsi::Object &newStyle) const; void maybeUpdateWindowDimensions( const facebook::react::ShadowViewMutation &mutation) const; - ShadowView createLayoutAnimation(ShadowView &before, const ShadowView& after, const Tag parentTag) const; + ShadowView createLayoutAnimation( + ShadowView &before, + const ShadowView &after, + const Tag parentTag) const; bool startAnimationsRecursively( std::shared_ptr node, diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsUtils.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsUtils.h index 3b0295ac16df..58c2d8aedca0 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsUtils.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsUtils.h @@ -168,14 +168,14 @@ static inline void updateLayoutMetrics( } } -static inline bool isRNSScreenOrStack(std::shared_ptr& node) { +static inline bool isRNSScreenOrStack(std::shared_ptr &node) { const auto &componentName = node->current.componentName; return !std::strcmp(componentName, "RNSScreenStack") || !std::strcmp(componentName, "RNSScreen") || !std::strcmp(componentName, "RNSModalScreen"); } -static inline bool isRNSScreen(std::shared_ptr& node) { +static inline bool isRNSScreen(std::shared_ptr &node) { const auto &componentName = node->current.componentName; return !std::strcmp(componentName, "RNSScreen") || !std::strcmp(componentName, "RNSModalScreen"); From a426c494ec4a6024eb2c6a4d4d424519469c4f58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Tue, 30 Sep 2025 10:57:06 +0200 Subject: [PATCH 56/90] transition containr update --- .../reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index b25da77bbd85..301e8338d01a 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -537,6 +537,8 @@ void LayoutAnimationsProxy::handleSharedTransitionsStart( lightNodes_[myTag] = node; root->children.push_back(node); containerTag = myTag; + sharedTransitionManager_->groups_[sharedTag].fakeTag = containerTag; + myTag += 2; } layoutAnimationsManager_->getConfigsForType( LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[containerTag] = @@ -548,10 +550,6 @@ void LayoutAnimationsProxy::handleSharedTransitionsStart( copy2.tag = containerTag; startSharedTransition(containerTag, copy2, copy, surfaceId); restoreMap_[containerTag][1] = after.tag; - if (shouldCreateContainer) { - sharedTransitionManager_->groups_[sharedTag].fakeTag = myTag; - myTag += 2; - } } } else if (!mutations.empty()) { for (auto &[sharedTag, transition] : transitions_) { From 36178c80c1610d8a59b3a3346fe55c123472f0cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Tue, 30 Sep 2025 11:36:38 +0200 Subject: [PATCH 57/90] cleanup --- .../LayoutAnimationsManager.h | 2 +- .../LayoutAnimationsProxy.cpp | 77 +++++++++---------- .../LayoutAnimations/LayoutAnimationsProxy.h | 2 + 3 files changed, 41 insertions(+), 40 deletions(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.h index 9142f2d0f987..d960af2323db 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.h @@ -27,7 +27,7 @@ struct SharedTransitionGroup { // std::string name; // std::optional current; std::unordered_map tagToView_; - Tag fakeTag = -1; + Tag containerTag = -1; }; struct Transition { diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index 301e8338d01a..20e41dbf6727 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -388,7 +388,7 @@ void LayoutAnimationsProxy::handleProgressTransition( startProgressTransition(myTag, copy2, copy, surfaceId); restoreMap_[myTag][0] = before.tag; restoreMap_[myTag][1] = after.tag; - sharedTransitionManager_->groups_[sharedTag].fakeTag = myTag; + sharedTransitionManager_->groups_[sharedTag].containerTag = myTag; activeTransitions_.insert(myTag); myTag += 2; } @@ -492,6 +492,15 @@ void LayoutAnimationsProxy::overrideTransform( shadowView.props = newProps; } +void LayoutAnimationsProxy::transferConfigToContainer( + Tag containerTag, + Tag beforeTag) const { + layoutAnimationsManager_->getConfigsForType( + LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[containerTag] = + layoutAnimationsManager_->getConfigsForType( + LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[beforeTag]; +} + void LayoutAnimationsProxy::handleSharedTransitionsStart( const LightNode::Unshared &afterTopScreen, const LightNode::Unshared &beforeTopScreen, @@ -508,62 +517,52 @@ void LayoutAnimationsProxy::handleSharedTransitionsStart( if (beforeTopScreen != afterTopScreen) { for (auto &[sharedTag, transition] : transitions_) { - const auto &[before, after] = transition.snapshot; - const auto &[beforeParentTag, afterParentTag] = transition.parentTag; - auto containerTag = sharedTransitionManager_->groups_[sharedTag].fakeTag; + auto &[before, after] = transition.snapshot; + // const auto &[beforeParentTag, afterParentTag] = + // transition.parentTag; + auto containerTag = + sharedTransitionManager_->groups_[sharedTag].containerTag; auto shouldCreateContainer = (containerTag == -1 || !layoutAnimations_.contains(containerTag)); if (shouldCreateContainer) { auto &root = lightNodes_[surfaceId]; - ShadowView s = before; - - s.tag = myTag; + ShadowView container = before; + containerTag = myTag; - filteredMutations.push_back(ShadowViewMutation::CreateMutation(s)); - filteredMutations.push_back(ShadowViewMutation::InsertMutation( - surfaceId, s, root->children.size())); + container.tag = containerTag; filteredMutations.push_back( - ShadowViewMutation::UpdateMutation(after, after, afterParentTag)); - auto m = - ShadowViewMutation::UpdateMutation(after, after, afterParentTag); - m = ShadowViewMutation::UpdateMutation( - after, - cloneViewWithoutOpacity(after, propsParserContext), - afterParentTag); - filteredMutations.push_back(m); + ShadowViewMutation::CreateMutation(container)); + filteredMutations.push_back(ShadowViewMutation::InsertMutation( + surfaceId, container, root->children.size())); auto node = std::make_shared(); - node->current = s; - lightNodes_[myTag] = node; + node->current = std::move(container); root->children.push_back(node); - containerTag = myTag; - sharedTransitionManager_->groups_[sharedTag].fakeTag = containerTag; + lightNodes_[myTag] = std::move(node); + + sharedTransitionManager_->groups_[sharedTag].containerTag = + containerTag; myTag += 2; } - layoutAnimationsManager_->getConfigsForType( - LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[containerTag] = - layoutAnimationsManager_->getConfigsForType( - LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[before.tag]; - ShadowView copy = after; - copy.tag = containerTag; - auto copy2 = before; - copy2.tag = containerTag; - startSharedTransition(containerTag, copy2, copy, surfaceId); + transferConfigToContainer(containerTag, before.tag); restoreMap_[containerTag][1] = after.tag; + before.tag = containerTag; + after.tag = containerTag; + startSharedTransition(containerTag, before, after, surfaceId); } } else if (!mutations.empty()) { for (auto &[sharedTag, transition] : transitions_) { - const auto &[_, after] = transition.snapshot; + auto &[_, after] = transition.snapshot; - auto copy = after; - auto fakeTag = sharedTransitionManager_->groups_[sharedTag].fakeTag; - copy.tag = fakeTag; - if (!layoutAnimations_.contains(fakeTag)) { + auto containerTag = + sharedTransitionManager_->groups_[sharedTag].containerTag; + if (!layoutAnimations_.contains(containerTag)) { continue; } - auto &la = layoutAnimations_[fakeTag]; - if (la.finalView.layoutMetrics != copy.layoutMetrics) { - startSharedTransition(fakeTag, copy, copy, surfaceId); + after.tag = containerTag; + auto &la = layoutAnimations_[containerTag]; + if (la.finalView.layoutMetrics != after.layoutMetrics) { + startSharedTransition(containerTag, la.currentView, after, surfaceId); } } } diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h index 151926f46f9d..4b8d034190cd 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h @@ -152,6 +152,8 @@ struct LayoutAnimationsProxy std::vector getAbsolutePositionsForRootPathView( const LightNode::Unshared &node) const; + void transferConfigToContainer(Tag containerTag, Tag beforeTag) const; + void overrideTransform( ShadowView &shadowView, const Transform &transform, From f40062679f530f6a549cc2b9ff3f7cfddf5b90c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Tue, 30 Sep 2025 11:41:41 +0200 Subject: [PATCH 58/90] extract container creation --- .../LayoutAnimationsProxy.cpp | 54 ++++++++++--------- .../LayoutAnimations/LayoutAnimationsProxy.h | 2 + 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index 20e41dbf6727..7ee311bfe502 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -501,6 +501,31 @@ void LayoutAnimationsProxy::transferConfigToContainer( LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[beforeTag]; } +Tag LayoutAnimationsProxy::getOrCreateContainer(const ShadowView& before, SharedTag sharedTag, ShadowViewMutationList &filteredMutations, SurfaceId surfaceId) const { + auto containerTag = + sharedTransitionManager_->groups_[sharedTag].containerTag; + auto shouldCreateContainer = + (containerTag == -1 || !layoutAnimations_.contains(containerTag)); + + if (shouldCreateContainer) { + auto &root = lightNodes_[surfaceId]; + ShadowView container = before; + containerTag = myTag; + + container.tag = containerTag; + filteredMutations.push_back(ShadowViewMutation::CreateMutation(container)); + filteredMutations.push_back(ShadowViewMutation::InsertMutation(surfaceId, container, root->children.size())); + auto node = std::make_shared(); + node->current = std::move(container); + root->children.push_back(node); + lightNodes_[myTag] = std::move(node); + + sharedTransitionManager_->groups_[sharedTag].containerTag = containerTag; + myTag += 2; + } + return containerTag; +} + void LayoutAnimationsProxy::handleSharedTransitionsStart( const LightNode::Unshared &afterTopScreen, const LightNode::Unshared &beforeTopScreen, @@ -518,36 +543,13 @@ void LayoutAnimationsProxy::handleSharedTransitionsStart( if (beforeTopScreen != afterTopScreen) { for (auto &[sharedTag, transition] : transitions_) { auto &[before, after] = transition.snapshot; - // const auto &[beforeParentTag, afterParentTag] = - // transition.parentTag; - auto containerTag = - sharedTransitionManager_->groups_[sharedTag].containerTag; - auto shouldCreateContainer = - (containerTag == -1 || !layoutAnimations_.contains(containerTag)); - - if (shouldCreateContainer) { - auto &root = lightNodes_[surfaceId]; - ShadowView container = before; - containerTag = myTag; - - container.tag = containerTag; - filteredMutations.push_back( - ShadowViewMutation::CreateMutation(container)); - filteredMutations.push_back(ShadowViewMutation::InsertMutation( - surfaceId, container, root->children.size())); - auto node = std::make_shared(); - node->current = std::move(container); - root->children.push_back(node); - lightNodes_[myTag] = std::move(node); - - sharedTransitionManager_->groups_[sharedTag].containerTag = - containerTag; - myTag += 2; - } + auto containerTag = getOrCreateContainer(before, sharedTag, filteredMutations, surfaceId); + transferConfigToContainer(containerTag, before.tag); restoreMap_[containerTag][1] = after.tag; before.tag = containerTag; after.tag = containerTag; + startSharedTransition(containerTag, before, after, surfaceId); } } else if (!mutations.empty()) { diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h index 4b8d034190cd..6de00e7041cd 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h @@ -153,6 +153,8 @@ struct LayoutAnimationsProxy const LightNode::Unshared &node) const; void transferConfigToContainer(Tag containerTag, Tag beforeTag) const; + + Tag getOrCreateContainer(const ShadowView& before, SharedTag sharedTag, ShadowViewMutationList &filteredMutations, SurfaceId surfaceId) const; void overrideTransform( ShadowView &shadowView, From c00c387682aebfee11f310329ccde914a1038cd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Tue, 30 Sep 2025 12:38:55 +0200 Subject: [PATCH 59/90] cleanup progress transition --- .../LayoutAnimationsProxy.cpp | 91 +++++++------------ .../LayoutAnimations/LayoutAnimationsProxy.h | 8 +- 2 files changed, 40 insertions(+), 59 deletions(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index 7ee311bfe502..85560f8c7c4b 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -344,54 +344,26 @@ void LayoutAnimationsProxy::handleProgressTransition( auto root = lightNodes_[surfaceId]; auto beforeTopScreen = topScreen[surfaceId]; auto afterTopScreen = lightNodes_[transitionTag_]; - if (beforeTopScreen && afterTopScreen) { + if (beforeTopScreen && afterTopScreen && + beforeTopScreen != afterTopScreen) { findSharedElementsOnScreen(beforeTopScreen, 0, propsParserContext); findSharedElementsOnScreen(afterTopScreen, 1, propsParserContext); - - if (beforeTopScreen->current.tag != afterTopScreen->current.tag) { - for (auto &[sharedTag, transition] : transitions_) { - const auto &[before, after] = transition.snapshot; - const auto &[beforeParentTag, afterParentTag] = transition.parentTag; - - auto &root = lightNodes_[surfaceId]; - ShadowView s = before; - s.tag = myTag; - filteredMutations.push_back(ShadowViewMutation::CreateMutation(s)); - filteredMutations.push_back(ShadowViewMutation::InsertMutation( - surfaceId, s, root->children.size())); - filteredMutations.push_back( - ShadowViewMutation::UpdateMutation(after, after, afterParentTag)); - auto p = lightNodes_[before.tag]->parent.lock(); - filteredMutations.push_back(ShadowViewMutation::UpdateMutation( - before, - cloneViewWithoutOpacity(before, propsParserContext), - p->current.tag)); - - auto m = ShadowViewMutation::UpdateMutation( - after, - cloneViewWithoutOpacity(after, propsParserContext), - afterParentTag); - filteredMutations.push_back(m); - auto node = std::make_shared(); - node->current = s; - lightNodes_[myTag] = node; - - root->children.push_back(node); - layoutAnimationsManager_->getConfigsForType( - LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[myTag] = - layoutAnimationsManager_->getConfigsForType( - LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[before.tag]; - ShadowView copy = after; - copy.tag = myTag; - auto copy2 = before; - copy2.tag = myTag; - startProgressTransition(myTag, copy2, copy, surfaceId); - restoreMap_[myTag][0] = before.tag; - restoreMap_[myTag][1] = after.tag; - sharedTransitionManager_->groups_[sharedTag].containerTag = myTag; - activeTransitions_.insert(myTag); - myTag += 2; - } + hideTransitioningViews(0, filteredMutations, propsParserContext); + hideTransitioningViews(1, filteredMutations, propsParserContext); + + for (auto &[sharedTag, transition] : transitions_) { + auto &[before, after] = transition.snapshot; + auto containerTag = getOrCreateContainer( + before, sharedTag, filteredMutations, surfaceId); + transferConfigToContainer(containerTag, before.tag); + + restoreMap_[containerTag][0] = before.tag; + restoreMap_[containerTag][1] = after.tag; + before.tag = containerTag; + after.tag = containerTag; + activeTransitions_.insert(containerTag); + + startProgressTransition(containerTag, before, after, surfaceId); } } } else if (transitionState_ == ACTIVE) { @@ -501,25 +473,29 @@ void LayoutAnimationsProxy::transferConfigToContainer( LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[beforeTag]; } -Tag LayoutAnimationsProxy::getOrCreateContainer(const ShadowView& before, SharedTag sharedTag, ShadowViewMutationList &filteredMutations, SurfaceId surfaceId) const { - auto containerTag = - sharedTransitionManager_->groups_[sharedTag].containerTag; +Tag LayoutAnimationsProxy::getOrCreateContainer( + const ShadowView &before, + SharedTag sharedTag, + ShadowViewMutationList &filteredMutations, + SurfaceId surfaceId) const { + auto containerTag = sharedTransitionManager_->groups_[sharedTag].containerTag; auto shouldCreateContainer = - (containerTag == -1 || !layoutAnimations_.contains(containerTag)); - + (containerTag == -1 || !layoutAnimations_.contains(containerTag)); + if (shouldCreateContainer) { auto &root = lightNodes_[surfaceId]; ShadowView container = before; containerTag = myTag; - + container.tag = containerTag; filteredMutations.push_back(ShadowViewMutation::CreateMutation(container)); - filteredMutations.push_back(ShadowViewMutation::InsertMutation(surfaceId, container, root->children.size())); + filteredMutations.push_back(ShadowViewMutation::InsertMutation( + surfaceId, container, root->children.size())); auto node = std::make_shared(); node->current = std::move(container); root->children.push_back(node); lightNodes_[myTag] = std::move(node); - + sharedTransitionManager_->groups_[sharedTag].containerTag = containerTag; myTag += 2; } @@ -543,13 +519,14 @@ void LayoutAnimationsProxy::handleSharedTransitionsStart( if (beforeTopScreen != afterTopScreen) { for (auto &[sharedTag, transition] : transitions_) { auto &[before, after] = transition.snapshot; - auto containerTag = getOrCreateContainer(before, sharedTag, filteredMutations, surfaceId); - + auto containerTag = + getOrCreateContainer(before, sharedTag, filteredMutations, surfaceId); + transferConfigToContainer(containerTag, before.tag); restoreMap_[containerTag][1] = after.tag; before.tag = containerTag; after.tag = containerTag; - + startSharedTransition(containerTag, before, after, surfaceId); } } else if (!mutations.empty()) { diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h index 6de00e7041cd..366c29822b8a 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h @@ -153,8 +153,12 @@ struct LayoutAnimationsProxy const LightNode::Unshared &node) const; void transferConfigToContainer(Tag containerTag, Tag beforeTag) const; - - Tag getOrCreateContainer(const ShadowView& before, SharedTag sharedTag, ShadowViewMutationList &filteredMutations, SurfaceId surfaceId) const; + + Tag getOrCreateContainer( + const ShadowView &before, + SharedTag sharedTag, + ShadowViewMutationList &filteredMutations, + SurfaceId surfaceId) const; void overrideTransform( ShadowView &shadowView, From 5814584146b5ef09879846d2feec6b1fed0556f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Tue, 30 Sep 2025 12:53:55 +0200 Subject: [PATCH 60/90] cleanup --- .../LayoutAnimations/LayoutAnimationsProxy.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index 85560f8c7c4b..75304d36505d 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -490,7 +490,7 @@ Tag LayoutAnimationsProxy::getOrCreateContainer( container.tag = containerTag; filteredMutations.push_back(ShadowViewMutation::CreateMutation(container)); filteredMutations.push_back(ShadowViewMutation::InsertMutation( - surfaceId, container, root->children.size())); + surfaceId, container, (int)root->children.size())); auto node = std::make_shared(); node->current = std::move(container); root->children.push_back(node); @@ -705,8 +705,7 @@ std::optional LayoutAnimationsProxy::endLayoutAnimation( layoutAnimations_.erase(tag); updateMap.erase(tag); - if (tag >= 10000) { - // TODO fix + if (sharedTransitionManager_->tagToName_.contains(tag)) { auto sharedTag = sharedTransitionManager_->tagToName_[tag]; sharedTransitionManager_->groups_.erase(sharedTag); @@ -742,7 +741,7 @@ void LayoutAnimationsProxy::handleRemovals( current = layoutAnimations_.at(node->current.tag).currentView; } filteredMutations.push_back(ShadowViewMutation::InsertMutation( - parent->current.tag, current, parent->children.size())); + parent->current.tag, current, (int)parent->children.size())); parent->children.push_back(node); parent->animatedChildrenCount++; if (node->state == UNDEFINED) { @@ -810,7 +809,7 @@ void LayoutAnimationsProxy::endAnimationsRecursively( // iterate from the end, so that children // with higher indices appear first in the mutations list - int i = node->children.size() - 1; + auto i = (int)node->children.size() - 1; for (auto it = node->children.rbegin(); it != node->children.rend(); it++) { auto &subNode = *it; if (subNode->state != DELETED) { @@ -884,7 +883,7 @@ bool LayoutAnimationsProxy::startAnimationsRecursively( // iterate from the end, so that children // with higher indices appear first in the mutations list - auto index = node->children.size(); + auto index = (int)node->children.size(); for (auto it = node->children.rbegin(); it != node->children.rend(); it++) { index--; auto &subNode = *it; From 8f0d82c4ee894f4af933cc39fe81302ede52161f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Tue, 14 Oct 2025 14:56:48 +0200 Subject: [PATCH 61/90] cleanup --- .../LayoutAnimationsManager.h | 16 ++-------- .../LayoutAnimationsProxy.cpp | 29 +++++++++++-------- .../LayoutAnimations/LayoutAnimationsProxy.h | 2 +- 3 files changed, 21 insertions(+), 26 deletions(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.h index d960af2323db..517c0a3a9cd6 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -23,27 +24,16 @@ using namespace facebook; using namespace worklets; using SharedTag = std::string; -struct SharedTransitionGroup { - // std::string name; - // std::optional current; - std::unordered_map tagToView_; - Tag containerTag = -1; -}; - struct Transition { ShadowView snapshot[2]; Tag parentTag[2] = {0, 0}; + std::optional transform[2]; }; struct SharedTransitionManager { - std::unordered_map groups_; + std::unordered_map containerTags_; std::unordered_map tagToName_; std::unordered_map nativeIDToName_; - - // SharedTransitionGroup getGroupForTag(); - // int createTransitionContainer(SharedTag sharedTag); - // int removeTransitionContainer(SharedTag sharedTag); - // std::vector> startBackTransition(); }; using TransitionMap = std::unordered_map; diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index 75304d36505d..548696595d19 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -308,14 +308,15 @@ void LayoutAnimationsProxy::findSharedElementsOnScreen( auto sharedTag = sharedTransitionManager_->tagToName_[node->current.tag]; auto &transition = transitionMap_[sharedTag]; auto transform = parseParentTransforms(node, absolutePositions); - if (transform) { - overrideTransform(copy, *transform, propsParserContext); - } + transition.transform[index] = std::move(transform); transition.snapshot[index] = copy; transition.parentTag[index] = node->parent.lock()->current.tag; if (transition.parentTag[0] && transition.parentTag[1]) { - transitions_.push_back({sharedTag, transition}); + // TODO: performance - this is costly on android + overrideTransform(transition.snapshot[0], transition.transform[0], propsParserContext); + overrideTransform(transition.snapshot[1], transition.transform[1], propsParserContext); + transitions_.emplace_back(sharedTag, transition); } else if (transition.parentTag[1]) { // TODO: this is too eager tagsToRestore_.push_back(transition.snapshot[1].tag); @@ -431,7 +432,7 @@ void LayoutAnimationsProxy::handleProgressTransition( topScreen[surfaceId] = lightNodes_[transitionTag_]; synchronized_ = false; } - sharedTransitionManager_->groups_.clear(); + sharedTransitionManager_->containerTags_.clear(); activeTransitions_.clear(); transitionState_ = NONE; } @@ -439,11 +440,14 @@ void LayoutAnimationsProxy::handleProgressTransition( void LayoutAnimationsProxy::overrideTransform( ShadowView &shadowView, - const Transform &transform, + const std::optional &transform, const PropsParserContext &propsParserContext) const { + if (!transform) { + return; + } #ifdef ANDROID auto array = folly::dynamic::array( - folly::dynamic::object("matrix", transform.operator folly::dynamic())); + folly::dynamic::object("matrix", transform->operator folly::dynamic())); folly::dynamic newTransformDynamic = folly::dynamic::object("transform", array); auto newRawProps = @@ -459,7 +463,7 @@ void LayoutAnimationsProxy::overrideTransform( .cloneProps(propsParserContext, shadowView.props, {}); auto viewProps = std::const_pointer_cast( std::static_pointer_cast(newProps)); - viewProps->transform = transform; + viewProps->transform = *transform; #endif shadowView.props = newProps; } @@ -478,7 +482,7 @@ Tag LayoutAnimationsProxy::getOrCreateContainer( SharedTag sharedTag, ShadowViewMutationList &filteredMutations, SurfaceId surfaceId) const { - auto containerTag = sharedTransitionManager_->groups_[sharedTag].containerTag; + auto containerTag = sharedTransitionManager_->containerTags_[sharedTag]; auto shouldCreateContainer = (containerTag == -1 || !layoutAnimations_.contains(containerTag)); @@ -486,6 +490,7 @@ Tag LayoutAnimationsProxy::getOrCreateContainer( auto &root = lightNodes_[surfaceId]; ShadowView container = before; containerTag = myTag; + sharedTransitionManager_->tagToName_[containerTag] = sharedTag; container.tag = containerTag; filteredMutations.push_back(ShadowViewMutation::CreateMutation(container)); @@ -496,7 +501,7 @@ Tag LayoutAnimationsProxy::getOrCreateContainer( root->children.push_back(node); lightNodes_[myTag] = std::move(node); - sharedTransitionManager_->groups_[sharedTag].containerTag = containerTag; + sharedTransitionManager_->containerTags_[sharedTag] = containerTag; myTag += 2; } return containerTag; @@ -534,7 +539,7 @@ void LayoutAnimationsProxy::handleSharedTransitionsStart( auto &[_, after] = transition.snapshot; auto containerTag = - sharedTransitionManager_->groups_[sharedTag].containerTag; + sharedTransitionManager_->containerTags_[sharedTag]; if (!layoutAnimations_.contains(containerTag)) { continue; } @@ -707,7 +712,7 @@ std::optional LayoutAnimationsProxy::endLayoutAnimation( if (sharedTransitionManager_->tagToName_.contains(tag)) { auto sharedTag = sharedTransitionManager_->tagToName_[tag]; - sharedTransitionManager_->groups_.erase(sharedTag); + sharedTransitionManager_->containerTags_.erase(sharedTag); sharedContainersToRemove_.push_back(tag); tagsToRestore_.push_back(restoreMap_[tag][1]); diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h index 366c29822b8a..6cdbc45cfab6 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h @@ -162,7 +162,7 @@ struct LayoutAnimationsProxy void overrideTransform( ShadowView &shadowView, - const Transform &transform, + const std::optional &transform, const PropsParserContext &propsParserContext) const; std::optional parseParentTransforms( From 255198456862e9c095bbf3c9c6546ddc938cab77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Wed, 15 Oct 2025 16:26:13 +0200 Subject: [PATCH 62/90] Fix for iOS snapshot --- .../LayoutAnimationsManager.h | 2 +- .../LayoutAnimationsProxy.cpp | 45 ++++++++++++++----- .../LayoutAnimations/LayoutAnimationsProxy.h | 15 ++++++- .../NativeModules/ReanimatedModuleProxy.cpp | 8 ++++ .../NativeModules/ReanimatedModuleProxy.h | 4 +- .../Tools/PlatformDepMethodsHolder.h | 4 ++ .../apple/reanimated/apple/REANodesManager.h | 3 ++ .../reanimated/apple/ReanimatedModule.mm | 1 + .../native/PlatformDepMethodsHolderImpl.mm | 22 +++++++++ 9 files changed, 91 insertions(+), 13 deletions(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.h index 517c0a3a9cd6..be7d447b3f4c 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.h @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #include #include diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index 548696595d19..5ab7cce06d34 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -35,6 +35,7 @@ std::optional LayoutAnimationsProxy::pullTransaction( ReanimatedSystraceSection d("pullTransaction"); PropsParserContext propsParserContext{surfaceId, *contextContainer_}; ShadowViewMutationList filteredMutations; + auto rootChildCount = (int)lightNodes_[surfaceId]->children.size(); std::vector> roots; bool isInTransition = transitionState_; @@ -63,6 +64,9 @@ std::optional LayoutAnimationsProxy::pullTransaction( topScreen[surfaceId] = afterTopScreen; if (afterTopScreen) { findSharedElementsOnScreen(afterTopScreen, 1, propsParserContext); +#ifdef __APPLE__ + forceScreenSnapshot_(afterTopScreen->current.tag); +#endif } bool shouldTransitionStart = beforeTopScreen && afterTopScreen && beforeTopScreen != afterTopScreen; @@ -111,6 +115,8 @@ std::optional LayoutAnimationsProxy::pullTransaction( transitionMap_.clear(); transitions_.clear(); + insertContainers(filteredMutations, rootChildCount, surfaceId); + return MountingTransaction{ surfaceId, transactionNumber, std::move(filteredMutations), telemetry}; } @@ -314,8 +320,10 @@ void LayoutAnimationsProxy::findSharedElementsOnScreen( if (transition.parentTag[0] && transition.parentTag[1]) { // TODO: performance - this is costly on android - overrideTransform(transition.snapshot[0], transition.transform[0], propsParserContext); - overrideTransform(transition.snapshot[1], transition.transform[1], propsParserContext); + overrideTransform( + transition.snapshot[0], transition.transform[0], propsParserContext); + overrideTransform( + transition.snapshot[1], transition.transform[1], propsParserContext); transitions_.emplace_back(sharedTag, transition); } else if (transition.parentTag[1]) { // TODO: this is too eager @@ -442,9 +450,9 @@ void LayoutAnimationsProxy::overrideTransform( ShadowView &shadowView, const std::optional &transform, const PropsParserContext &propsParserContext) const { - if (!transform) { - return; - } + if (!transform) { + return; + } #ifdef ANDROID auto array = folly::dynamic::array( folly::dynamic::object("matrix", transform->operator folly::dynamic())); @@ -493,12 +501,13 @@ Tag LayoutAnimationsProxy::getOrCreateContainer( sharedTransitionManager_->tagToName_[containerTag] = sharedTag; container.tag = containerTag; - filteredMutations.push_back(ShadowViewMutation::CreateMutation(container)); - filteredMutations.push_back(ShadowViewMutation::InsertMutation( - surfaceId, container, (int)root->children.size())); + // filteredMutations.push_back(ShadowViewMutation::CreateMutation(container)); + // filteredMutations.push_back(ShadowViewMutation::InsertMutation( + // surfaceId, container, (int)root->children.size())); auto node = std::make_shared(); node->current = std::move(container); root->children.push_back(node); + containersToInsert_.push_back(node); lightNodes_[myTag] = std::move(node); sharedTransitionManager_->containerTags_[sharedTag] = containerTag; @@ -538,8 +547,7 @@ void LayoutAnimationsProxy::handleSharedTransitionsStart( for (auto &[sharedTag, transition] : transitions_) { auto &[_, after] = transition.snapshot; - auto containerTag = - sharedTransitionManager_->containerTags_[sharedTag]; + auto containerTag = sharedTransitionManager_->containerTags_[sharedTag]; if (!layoutAnimations_.contains(containerTag)) { continue; } @@ -646,6 +654,23 @@ std::optional LayoutAnimationsProxy::onGestureCancel() { return {}; } +void LayoutAnimationsProxy::insertContainers( + ShadowViewMutationList &filteredMutations, + int &rootChildCount, + SurfaceId surfaceId) const { + ShadowViewMutationList temp = std::move(filteredMutations); + filteredMutations.reserve(containersToInsert_.size() * 2); + auto root = lightNodes_[surfaceId]; + for (auto &node : containersToInsert_) { + filteredMutations.push_back( + ShadowViewMutation::CreateMutation(node->current)); + filteredMutations.push_back(ShadowViewMutation::InsertMutation( + surfaceId, node->current, rootChildCount++)); + } + filteredMutations.insert(filteredMutations.end(), temp.begin(), temp.end()); + containersToInsert_.clear(); +} + // MARK: Layout Animation Updates std::optional LayoutAnimationsProxy::progressLayoutAnimation( diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h index 6cdbc45cfab6..17a4ac6b8a06 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h @@ -2,6 +2,7 @@ #include #include +#include #include @@ -58,10 +59,11 @@ struct LayoutAnimationsProxy // mutable std::unordered_map< // mutable std::optional previousView; mutable std::unordered_map> lightNodes_; - + mutable std::vector containersToInsert_; mutable std::unordered_map transformForNode_; mutable std::vector finishedAnimationTags_; + mutable ForceScreenSnapshotFunction forceScreenSnapshot_; std::shared_ptr layoutAnimationsManager_; std::shared_ptr contextContainer_; SharedComponentDescriptorRegistry componentDescriptorRegistry_; @@ -121,6 +123,12 @@ struct LayoutAnimationsProxy const PropsParserContext &propsParserContext, SurfaceId surfaceId) const; +#ifdef __APPLE__ + void setForceScreenSnapshotFunction(ForceScreenSnapshotFunction f) { + forceScreenSnapshot_ = std::move(f); + } +#endif + void hideTransitioningViews( int index, ShadowViewMutationList &filteredMutations, @@ -149,6 +157,11 @@ struct LayoutAnimationsProxy int index, const PropsParserContext &propsParserContext) const; + void insertContainers( + ShadowViewMutationList &filteredMutations, + int &rootChildCount, + SurfaceId surfaceId) const; + std::vector getAbsolutePositionsForRootPathView( const LightNode::Unshared &node) const; diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp index 80af24a31c01..a6d79b4de2cd 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp @@ -52,6 +52,10 @@ ReanimatedModuleProxy::ReanimatedModuleProxy( layoutAnimationsManager_( std::make_shared(jsLogger_)), getAnimationTimestamp_(platformDepMethodsHolder.getAnimationTimestamp), +#ifdef __APPLE__ + forceScreenSnapshot_( + platformDepMethodsHolder.forceScreenSnapshotFunction), +#endif animatedPropsRegistry_(std::make_shared()), staticPropsRegistry_(std::make_shared()), updatesRegistryManager_( @@ -1357,6 +1361,10 @@ void ReanimatedModuleProxy::initializeLayoutAnimationsProxy() { scheduler->getContextContainer(), workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(), workletsModuleProxy_->getUIScheduler()); +#ifdef __APPLE__ + layoutAnimationsProxy_->setForceScreenSnapshotFunction( + forceScreenSnapshot_); +#endif } } diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h index 785993ea3b0b..13dda93766e5 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h @@ -233,7 +233,9 @@ class ReanimatedModuleProxy const std::shared_ptr jsLogger_; std::shared_ptr layoutAnimationsManager_; GetAnimationTimestampFunction getAnimationTimestamp_; - +#ifdef __APPLE__ + ForceScreenSnapshotFunction forceScreenSnapshot_; +#endif bool cssLoopRunning_{false}; bool shouldUpdateCssAnimations_{true}; double currentCssTimestamp_{0}; diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/Tools/PlatformDepMethodsHolder.h b/packages/react-native-reanimated/Common/cpp/reanimated/Tools/PlatformDepMethodsHolder.h index dbea4baa50a9..848cf9854329 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/Tools/PlatformDepMethodsHolder.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/Tools/PlatformDepMethodsHolder.h @@ -46,11 +46,15 @@ using KeyboardEventSubscribeFunction = using KeyboardEventUnsubscribeFunction = std::function; using MaybeFlushUIUpdatesQueueFunction = std::function; +using ForceScreenSnapshotFunction = std::function; struct PlatformDepMethodsHolder { RequestRenderFunction requestRender; #ifdef ANDROID SynchronouslyUpdateUIPropsFunction synchronouslyUpdateUIPropsFunction; #endif // ANDROID +#ifdef __APPLE__ + ForceScreenSnapshotFunction forceScreenSnapshotFunction; +#endif GetAnimationTimestampFunction getAnimationTimestamp; RegisterSensorFunction registerSensor; UnregisterSensorFunction unregisterSensor; diff --git a/packages/react-native-reanimated/apple/reanimated/apple/REANodesManager.h b/packages/react-native-reanimated/apple/reanimated/apple/REANodesManager.h index f955d9e3854c..a3ac46b55fdd 100644 --- a/packages/react-native-reanimated/apple/reanimated/apple/REANodesManager.h +++ b/packages/react-native-reanimated/apple/reanimated/apple/REANodesManager.h @@ -1,6 +1,7 @@ #import #import +#import typedef void (^REAOnAnimationCallback)(READisplayLink *displayLink); typedef void (^REAEventHandler)(id event); @@ -9,6 +10,8 @@ typedef void (^REAPerformOperations)(); @interface REANodesManager : NSObject +@property (weak) RCTSurfacePresenter *surfacePresenter; + - (nonnull instancetype)init; - (void)invalidate; diff --git a/packages/react-native-reanimated/apple/reanimated/apple/ReanimatedModule.mm b/packages/react-native-reanimated/apple/reanimated/apple/ReanimatedModule.mm index 3018f0d7a657..b8e166d1aef7 100644 --- a/packages/react-native-reanimated/apple/reanimated/apple/ReanimatedModule.mm +++ b/packages/react-native-reanimated/apple/reanimated/apple/ReanimatedModule.mm @@ -87,6 +87,7 @@ - (void)setBridge:(RCTBridge *)bridge REAAssertJavaScriptQueue(); [super setBridge:bridge]; _nodesManager = [[REANodesManager alloc] init]; + _nodesManager.surfacePresenter = _surfacePresenter; [[self.moduleRegistry moduleForName:"EventDispatcher"] addDispatchObserver:self]; } diff --git a/packages/react-native-reanimated/apple/reanimated/apple/native/PlatformDepMethodsHolderImpl.mm b/packages/react-native-reanimated/apple/reanimated/apple/native/PlatformDepMethodsHolderImpl.mm index f4a7e52575d5..fea72a6c3fc8 100644 --- a/packages/react-native-reanimated/apple/reanimated/apple/native/PlatformDepMethodsHolderImpl.mm +++ b/packages/react-native-reanimated/apple/reanimated/apple/native/PlatformDepMethodsHolderImpl.mm @@ -6,6 +6,14 @@ #import #import #import +#import + +#import +#import +#import + +// TODO: should be conditional import +#import namespace reanimated { @@ -96,9 +104,22 @@ KeyboardEventUnsubscribeFunction makeUnsubscribeFromKeyboardEventsFunction(REAKe return unsubscribeFromKeyboardEventsFunction; } +ForceScreenSnapshotFunction makeForceScreenSnapshotFunction(REANodesManager *nodesManager){ + auto f = [=](Tag tag) { + RCTSurfacePresenter *surfacePresenter = nodesManager.surfacePresenter; + RCTComponentViewRegistry *componentViewRegistry = surfacePresenter.mountingManager.componentViewRegistry; + UIView *componentView = [componentViewRegistry findComponentViewWithTag:tag]; + RNSScreenView* rnsscreenview = (RNSScreenView*)componentView; + [rnsscreenview setSnapshotAfterUpdates:YES]; + }; + return f; +} + PlatformDepMethodsHolder makePlatformDepMethodsHolder(RCTModuleRegistry *moduleRegistry, REANodesManager *nodesManager) { auto requestRender = makeRequestRender(nodesManager); + + auto forceScreenSnapshotFunction = makeForceScreenSnapshotFunction(nodesManager); auto getAnimationTimestamp = makeGetAnimationTimestamp(); @@ -120,6 +141,7 @@ PlatformDepMethodsHolder makePlatformDepMethodsHolder(RCTModuleRegistry *moduleR PlatformDepMethodsHolder platformDepMethodsHolder = { requestRender, + forceScreenSnapshotFunction, getAnimationTimestamp, registerSensorFunction, unregisterSensorFunction, From c8f58aa09de15ee92084f1b31700ccbb9424a288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Thu, 16 Oct 2025 17:34:15 +0200 Subject: [PATCH 63/90] fix flicker --- .../LayoutAnimationsProxy.cpp | 21 ++++++++----------- .../LayoutAnimations/LayoutAnimationsProxy.h | 2 +- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index 5ab7cce06d34..b22ae3b5655c 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -101,16 +101,9 @@ std::optional LayoutAnimationsProxy::pullTransaction( exiting_.clear(); } - cleanupSharedTransitions(filteredMutations, propsParserContext, surfaceId); - addOngoingAnimations(surfaceId, filteredMutations); - for (const auto tag : finishedAnimationTags_) { - auto &updateMap = surfaceManager.getUpdateMap(surfaceId); - layoutAnimations_.erase(tag); - updateMap.erase(tag); - } - finishedAnimationTags_.clear(); + cleanupAnimations(filteredMutations, propsParserContext, surfaceId); transitionMap_.clear(); transitions_.clear(); @@ -560,7 +553,7 @@ void LayoutAnimationsProxy::handleSharedTransitionsStart( } } -void LayoutAnimationsProxy::cleanupSharedTransitions( +void LayoutAnimationsProxy::cleanupAnimations( ShadowViewMutationList &filteredMutations, const PropsParserContext &propsParserContext, SurfaceId surfaceId) const { @@ -592,6 +585,13 @@ void LayoutAnimationsProxy::cleanupSharedTransitions( } } sharedContainersToRemove_.clear(); + + for (const auto tag : finishedAnimationTags_) { + auto &updateMap = surfaceManager.getUpdateMap(surfaceId); + layoutAnimations_.erase(tag); + updateMap.erase(tag); + } + finishedAnimationTags_.clear(); } void LayoutAnimationsProxy::hideTransitioningViews( @@ -731,9 +731,6 @@ std::optional LayoutAnimationsProxy::endLayoutAnimation( } finishedAnimationTags_.push_back(tag); auto surfaceId = layoutAnimation.finalView.surfaceId; - auto &updateMap = surfaceManager.getUpdateMap(surfaceId); - layoutAnimations_.erase(tag); - updateMap.erase(tag); if (sharedTransitionManager_->tagToName_.contains(tag)) { auto sharedTag = sharedTransitionManager_->tagToName_[tag]; diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h index 17a4ac6b8a06..1a2346f07c24 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h @@ -118,7 +118,7 @@ struct LayoutAnimationsProxy const PropsParserContext &propsParserContext, SurfaceId surfaceId) const; - void cleanupSharedTransitions( + void cleanupAnimations( ShadowViewMutationList &filteredMutations, const PropsParserContext &propsParserContext, SurfaceId surfaceId) const; From de4c717aa25151ed94f6181f8864100c0cebfcd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Thu, 16 Oct 2025 17:34:40 +0200 Subject: [PATCH 64/90] Fix jumps on layout animation start --- .../cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp index b22ae3b5655c..0df6dd33e9e3 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -217,7 +217,8 @@ void LayoutAnimationsProxy::updateLightTree( // we are not starting the animation here because any update will come // from the UPDATE mutation // layout_.push_back(node); - filteredMutations.push_back(mutation); + // TODO: this introduces ghosting. Figure out proper reconciliation + filteredMutations.push_back(ShadowViewMutation::InsertMutation(mutation.parentTag, node->previous, mutation.index)); // node->previous = node->current; // node->current = mutation.newChildShadowView; } else if (layoutAnimationsManager_->hasLayoutAnimation( From b2ea86a00c1d5dc83a7c190669b6bf96811445bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Thu, 16 Oct 2025 19:05:42 +0200 Subject: [PATCH 65/90] oopsie --- .../cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h index 9e316cdbd3fc..13d43ac09369 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h @@ -30,8 +30,8 @@ struct LayoutAnimation { ShadowView finalView, currentView, startView; Tag parentTag; std::optional opacity; - bool isViewAlreadyMounted = false; int count = 1; + bool isViewAlreadyMounted = false; LayoutAnimation &operator=(const LayoutAnimation &other) = default; }; @@ -91,7 +91,9 @@ struct LayoutAnimationsProxy std::shared_ptr jsInvoker #endif ) - : layoutAnimationsManager_(layoutAnimationsManager), + : sharedTransitionManager_( + layoutAnimationsManager->sharedTransitionManager_), + layoutAnimationsManager_(layoutAnimationsManager), contextContainer_(contextContainer), componentDescriptorRegistry_(componentDescriptorRegistry), uiRuntime_(uiRuntime), From 7102201ded832d04f44ec3874c917329da4b339e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Fri, 17 Oct 2025 11:23:39 +0200 Subject: [PATCH 66/90] introduce feature flag --- apps/fabric-example/package.json | 3 +- .../Fabric/ReanimatedCommitHook.cpp | 19 +- .../reanimated/Fabric/ReanimatedCommitHook.h | 16 +- ...=> LayoutAnimationsProxy_Experimental.cpp} | 101 +- ...h => LayoutAnimationsProxy_Experimental.h} | 11 +- .../LayoutAnimationsProxy_Legacy.cpp | 1040 +++++++++++++++++ .../LayoutAnimationsProxy_Legacy.h | 331 ++++++ .../LayoutAnimationsUtils.cpp | 4 +- .../LayoutAnimations/LayoutAnimationsUtils.h | 4 +- .../NativeModules/ReanimatedModuleProxy.cpp | 137 ++- .../NativeModules/ReanimatedModuleProxy.h | 7 +- .../apple/reanimated/apple/REANodesManager.h | 2 +- .../src/featureFlags/staticFlags.json | 3 +- 13 files changed, 1560 insertions(+), 118 deletions(-) rename packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/{LayoutAnimationsProxy.cpp => LayoutAnimationsProxy_Experimental.cpp} (94%) rename packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/{LayoutAnimationsProxy.h => LayoutAnimationsProxy_Experimental.h} (97%) create mode 100644 packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Legacy.cpp create mode 100644 packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Legacy.h diff --git a/apps/fabric-example/package.json b/apps/fabric-example/package.json index da7c90228585..e2c743bc46fd 100644 --- a/apps/fabric-example/package.json +++ b/apps/fabric-example/package.json @@ -31,7 +31,8 @@ "IOS_SYNCHRONOUSLY_UPDATE_UI_PROPS": true, "EXPERIMENTAL_CSS_ANIMATIONS_FOR_SVG_COMPONENTS": true, "USE_SYNCHRONIZABLE_FOR_MUTABLES": true, - "USE_COMMIT_HOOK_ONLY_FOR_REACT_COMMITS": true + "USE_COMMIT_HOOK_ONLY_FOR_REACT_COMMITS": true, + "SHARED_ELEMENT_TRANSITIONS": true } }, "worklets": { diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/Fabric/ReanimatedCommitHook.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/Fabric/ReanimatedCommitHook.cpp index 1fde84303158..a83744dbf58e 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/Fabric/ReanimatedCommitHook.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/Fabric/ReanimatedCommitHook.cpp @@ -16,10 +16,15 @@ namespace reanimated { ReanimatedCommitHook::ReanimatedCommitHook( const std::shared_ptr &uiManager, const std::shared_ptr &updatesRegistryManager, - const std::shared_ptr &layoutAnimationsProxy) + const std::shared_ptr + &layoutAnimationsProxyLegacy, + const std::shared_ptr< + reanimated_experimental::LayoutAnimationsProxy_Experimental> + &layoutAnimationsProxyExperimental) : uiManager_(uiManager), updatesRegistryManager_(updatesRegistryManager), - layoutAnimationsProxy_(layoutAnimationsProxy) { + layoutAnimationsProxyExperimental_(layoutAnimationsProxyExperimental), + layoutAnimationsProxyLegacy_(layoutAnimationsProxyLegacy){ uiManager_->registerCommitHook(*this); } @@ -42,8 +47,14 @@ void ReanimatedCommitHook::maybeInitializeLayoutAnimations( // shouldn't invoke it twice for the same surface return; } - shadowTree.getMountingCoordinator()->setMountingOverrideDelegate( - strongThis->layoutAnimationsProxy_); + if constexpr (StaticFeatureFlags::getFlag( + "SHARED_ELEMENT_TRANSITIONS")) { + shadowTree.getMountingCoordinator()->setMountingOverrideDelegate( + strongThis->layoutAnimationsProxyExperimental_); + } else { + shadowTree.getMountingCoordinator()->setMountingOverrideDelegate( + strongThis->layoutAnimationsProxyLegacy_); + } }); currentMaxSurfaceId_ = surfaceId; } diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/Fabric/ReanimatedCommitHook.h b/packages/react-native-reanimated/Common/cpp/reanimated/Fabric/ReanimatedCommitHook.h index 1ca39bfe716a..e82d10377874 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/Fabric/ReanimatedCommitHook.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/Fabric/ReanimatedCommitHook.h @@ -1,7 +1,8 @@ #pragma once #include -#include +#include +#include #include @@ -17,8 +18,13 @@ class ReanimatedCommitHook public: ReanimatedCommitHook( const std::shared_ptr &uiManager, - const std::shared_ptr &updatesRegistryManager, - const std::shared_ptr &layoutAnimationsProxy); + const std::shared_ptr + &updatesRegistryManagerLegacy, + const std::shared_ptr + &layoutAnimationsProxy, + const std::shared_ptr< + reanimated_experimental::LayoutAnimationsProxy_Experimental> + &layoutAnimationsProxyExperimental); ~ReanimatedCommitHook() noexcept override; @@ -41,7 +47,9 @@ class ReanimatedCommitHook private: std::shared_ptr uiManager_; std::shared_ptr updatesRegistryManager_; - std::shared_ptr layoutAnimationsProxy_; + std::shared_ptr + layoutAnimationsProxyExperimental_; + std::shared_ptr layoutAnimationsProxyLegacy_; SurfaceId currentMaxSurfaceId_ = -1; diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Experimental.cpp similarity index 94% rename from packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp rename to packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Experimental.cpp index d64a262e6b22..cda2c5e79fa0 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Experimental.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #ifndef ANDROID @@ -22,11 +22,12 @@ using ScrollState = ConcreteState; -namespace reanimated { +namespace reanimated_experimental { // MARK: MountingOverrideDelegate -std::optional LayoutAnimationsProxy::pullTransaction( +std::optional +LayoutAnimationsProxy_Experimental::pullTransaction( SurfaceId surfaceId, MountingTransaction::Number transactionNumber, const TransactionTelemetry &telemetry, @@ -114,13 +115,13 @@ std::optional LayoutAnimationsProxy::pullTransaction( surfaceId, transactionNumber, std::move(filteredMutations), telemetry}; } -bool LayoutAnimationsProxy::shouldOverridePullTransaction() const { +bool LayoutAnimationsProxy_Experimental::shouldOverridePullTransaction() const { return true; } // MARK: Light Tree -void LayoutAnimationsProxy::updateLightTree( +void LayoutAnimationsProxy_Experimental::updateLightTree( const PropsParserContext &propsParserContext, const ShadowViewMutationList &mutations, ShadowViewMutationList &filteredMutations) const { @@ -263,7 +264,7 @@ void LayoutAnimationsProxy::updateLightTree( // MARK: Shared Element Transitions -LightNode::Unshared LayoutAnimationsProxy::findTopScreen( +LightNode::Unshared LayoutAnimationsProxy_Experimental::findTopScreen( LightNode::Unshared node) const { LightNode::Unshared result = nullptr; if (!node->current.componentName) { @@ -296,7 +297,7 @@ LightNode::Unshared LayoutAnimationsProxy::findTopScreen( return result; } -void LayoutAnimationsProxy::findSharedElementsOnScreen( +void LayoutAnimationsProxy_Experimental::findSharedElementsOnScreen( const LightNode::Unshared &node, int index, const PropsParserContext &propsParserContext) const { @@ -330,7 +331,7 @@ void LayoutAnimationsProxy::findSharedElementsOnScreen( } } -void LayoutAnimationsProxy::handleProgressTransition( +void LayoutAnimationsProxy_Experimental::handleProgressTransition( ShadowViewMutationList &filteredMutations, const ShadowViewMutationList &mutations, const PropsParserContext &propsParserContext, @@ -441,7 +442,7 @@ void LayoutAnimationsProxy::handleProgressTransition( } } -void LayoutAnimationsProxy::overrideTransform( +void LayoutAnimationsProxy_Experimental::overrideTransform( ShadowView &shadowView, const std::optional &transform, const PropsParserContext &propsParserContext) const { @@ -471,7 +472,7 @@ void LayoutAnimationsProxy::overrideTransform( shadowView.props = newProps; } -void LayoutAnimationsProxy::transferConfigToContainer( +void LayoutAnimationsProxy_Experimental::transferConfigToContainer( Tag containerTag, Tag beforeTag) const { layoutAnimationsManager_->getConfigsForType( @@ -480,7 +481,7 @@ void LayoutAnimationsProxy::transferConfigToContainer( LayoutAnimationType::SHARED_ELEMENT_TRANSITION)[beforeTag]; } -Tag LayoutAnimationsProxy::getOrCreateContainer( +Tag LayoutAnimationsProxy_Experimental::getOrCreateContainer( const ShadowView &before, SharedTag sharedTag, ShadowViewMutationList &filteredMutations, @@ -511,7 +512,7 @@ Tag LayoutAnimationsProxy::getOrCreateContainer( return containerTag; } -void LayoutAnimationsProxy::handleSharedTransitionsStart( +void LayoutAnimationsProxy_Experimental::handleSharedTransitionsStart( const LightNode::Unshared &afterTopScreen, const LightNode::Unshared &beforeTopScreen, ShadowViewMutationList &filteredMutations, @@ -519,7 +520,7 @@ void LayoutAnimationsProxy::handleSharedTransitionsStart( const PropsParserContext &propsParserContext, SurfaceId surfaceId) const { ReanimatedSystraceSection s1( - "LayoutAnimationsProxy::handleSharedTransitionsStart"); + "LayoutAnimationsProxy_Experimental::handleSharedTransitionsStart"); if (!beforeTopScreen || !afterTopScreen) { return; @@ -555,7 +556,7 @@ void LayoutAnimationsProxy::handleSharedTransitionsStart( } } -void LayoutAnimationsProxy::cleanupAnimations( +void LayoutAnimationsProxy_Experimental::cleanupAnimations( ShadowViewMutationList &filteredMutations, const PropsParserContext &propsParserContext, SurfaceId surfaceId) const { @@ -599,7 +600,7 @@ void LayoutAnimationsProxy::cleanupAnimations( finishedAnimationTags_.clear(); } -void LayoutAnimationsProxy::hideTransitioningViews( +void LayoutAnimationsProxy_Experimental::hideTransitioningViews( int index, ShadowViewMutationList &filteredMutations, const PropsParserContext &propsParserContext) const { @@ -614,7 +615,8 @@ void LayoutAnimationsProxy::hideTransitioningViews( } } -std::optional LayoutAnimationsProxy::onTransitionProgress( +std::optional +LayoutAnimationsProxy_Experimental::onTransitionProgress( int tag, double progress, bool isClosing, @@ -648,7 +650,7 @@ std::optional LayoutAnimationsProxy::onTransitionProgress( return {}; } -std::optional LayoutAnimationsProxy::onGestureCancel() { +std::optional LayoutAnimationsProxy_Experimental::onGestureCancel() { auto lock = std::unique_lock(mutex); if (transitionState_) { transitionState_ = CANCELLED; @@ -659,7 +661,7 @@ std::optional LayoutAnimationsProxy::onGestureCancel() { return {}; } -void LayoutAnimationsProxy::insertContainers( +void LayoutAnimationsProxy_Experimental::insertContainers( ShadowViewMutationList &filteredMutations, int &rootChildCount, SurfaceId surfaceId) const { @@ -678,9 +680,11 @@ void LayoutAnimationsProxy::insertContainers( // MARK: Layout Animation Updates -std::optional LayoutAnimationsProxy::progressLayoutAnimation( +std::optional +LayoutAnimationsProxy_Experimental::progressLayoutAnimation( int tag, const jsi::Object &newStyle) { + ReanimatedSystraceSection s("progressLayoutAnimation"); auto lock = std::unique_lock(mutex); auto layoutAnimationIt = layoutAnimations_.find(tag); @@ -714,7 +718,7 @@ std::optional LayoutAnimationsProxy::progressLayoutAnimation( return layoutAnimation.finalView.surfaceId; } -std::optional LayoutAnimationsProxy::endLayoutAnimation( +std::optional LayoutAnimationsProxy_Experimental::endLayoutAnimation( int tag, bool shouldRemove) { auto lock = std::unique_lock(mutex); @@ -756,7 +760,7 @@ std::optional LayoutAnimationsProxy::endLayoutAnimation( return surfaceId; } -void LayoutAnimationsProxy::handleRemovals( +void LayoutAnimationsProxy_Experimental::handleRemovals( ShadowViewMutationList &filteredMutations, std::vector> &roots) const { // iterate from the end, so that children @@ -806,7 +810,7 @@ void LayoutAnimationsProxy::handleRemovals( deadNodes.clear(); } -void LayoutAnimationsProxy::addOngoingAnimations( +void LayoutAnimationsProxy_Experimental::addOngoingAnimations( SurfaceId surfaceId, ShadowViewMutationList &mutations) const { auto &updateMap = surfaceManager.getUpdateMap(surfaceId); @@ -861,7 +865,7 @@ void LayoutAnimationsProxy::addOngoingAnimations( updateMap.clear(); } -void LayoutAnimationsProxy::endAnimationsRecursively( +void LayoutAnimationsProxy_Experimental::endAnimationsRecursively( std::shared_ptr node, int index, ShadowViewMutationList &mutations) const { @@ -883,7 +887,7 @@ void LayoutAnimationsProxy::endAnimationsRecursively( mutations.push_back(ShadowViewMutation::DeleteMutation(node->current)); } -void LayoutAnimationsProxy::maybeDropAncestors( +void LayoutAnimationsProxy_Experimental::maybeDropAncestors( std::shared_ptr parent, std::shared_ptr child, ShadowViewMutationList &cleanupMutations) const { @@ -914,12 +918,12 @@ void LayoutAnimationsProxy::maybeDropAncestors( } const ComponentDescriptor & -LayoutAnimationsProxy::getComponentDescriptorForShadowView( +LayoutAnimationsProxy_Experimental::getComponentDescriptorForShadowView( const ShadowView &shadowView) const { return componentDescriptorRegistry_->at(shadowView.componentHandle); } -bool LayoutAnimationsProxy::startAnimationsRecursively( +bool LayoutAnimationsProxy_Experimental::startAnimationsRecursively( std::shared_ptr node, bool shouldRemoveSubviewsWithoutAnimations, bool shouldAnimate, @@ -990,13 +994,14 @@ bool LayoutAnimationsProxy::startAnimationsRecursively( return wantAnimateExit; } -void LayoutAnimationsProxy::updateOngoingAnimationTarget( +void LayoutAnimationsProxy_Experimental::updateOngoingAnimationTarget( const int tag, const ShadowViewMutation &mutation) const { layoutAnimations_[tag].finalView = mutation.newChildShadowView; } -void LayoutAnimationsProxy::maybeCancelAnimation(const int tag) const { +void LayoutAnimationsProxy_Experimental::maybeCancelAnimation( + const int tag) const { if (!layoutAnimations_.contains(tag)) { return; } @@ -1012,7 +1017,7 @@ void LayoutAnimationsProxy::maybeCancelAnimation(const int tag) const { }); } -void LayoutAnimationsProxy::transferConfigFromNativeID( +void LayoutAnimationsProxy_Experimental::transferConfigFromNativeID( const std::string nativeIdString, const int tag) const { if (nativeIdString.empty()) { @@ -1029,7 +1034,7 @@ void LayoutAnimationsProxy::transferConfigFromNativeID( // 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 -ShadowView LayoutAnimationsProxy::cloneViewWithoutOpacity( +ShadowView LayoutAnimationsProxy_Experimental::cloneViewWithoutOpacity( const ShadowView &shadowView, const PropsParserContext &propsParserContext) const { auto newView = shadowView; @@ -1040,7 +1045,7 @@ ShadowView LayoutAnimationsProxy::cloneViewWithoutOpacity( return newView; } -ShadowView LayoutAnimationsProxy::cloneViewWithOpacity( +ShadowView LayoutAnimationsProxy_Experimental::cloneViewWithOpacity( const ShadowView &shadowView, const PropsParserContext &propsParserContext) const { auto newView = shadowView; @@ -1052,7 +1057,7 @@ ShadowView LayoutAnimationsProxy::cloneViewWithOpacity( return newView; } -void LayoutAnimationsProxy::maybeRestoreOpacity( +void LayoutAnimationsProxy_Experimental::maybeRestoreOpacity( LayoutAnimation &layoutAnimation, const jsi::Object &newStyle) const { if (layoutAnimation.opacity && !newStyle.hasProperty(uiRuntime_, "opacity")) { @@ -1067,7 +1072,7 @@ void LayoutAnimationsProxy::maybeRestoreOpacity( } } -void LayoutAnimationsProxy::maybeUpdateWindowDimensions( +void LayoutAnimationsProxy_Experimental::maybeUpdateWindowDimensions( const facebook::react::ShadowViewMutation &mutation) const { if (mutation.type == ShadowViewMutation::Update && !std::strcmp( @@ -1081,7 +1086,7 @@ void LayoutAnimationsProxy::maybeUpdateWindowDimensions( // MARK: Start Animation -ShadowView LayoutAnimationsProxy::createLayoutAnimation( +ShadowView LayoutAnimationsProxy_Experimental::createLayoutAnimation( ShadowView &before, const ShadowView &after, const Tag parentTag) const { @@ -1108,7 +1113,7 @@ ShadowView LayoutAnimationsProxy::createLayoutAnimation( return oldView; } -void LayoutAnimationsProxy::startEnteringAnimation( +void LayoutAnimationsProxy_Experimental::startEnteringAnimation( const LightNode::Unshared &node) const { auto newChildShadowView = node->current; auto finalView = newChildShadowView; @@ -1162,7 +1167,7 @@ void LayoutAnimationsProxy::startEnteringAnimation( }); } -void LayoutAnimationsProxy::startExitingAnimation( +void LayoutAnimationsProxy_Experimental::startExitingAnimation( const LightNode::Unshared &node) const { auto &oldChildShadowView = node->current; const auto surfaceId = oldChildShadowView.surfaceId; @@ -1206,7 +1211,7 @@ void LayoutAnimationsProxy::startExitingAnimation( }); } -void LayoutAnimationsProxy::startLayoutAnimation( +void LayoutAnimationsProxy_Experimental::startLayoutAnimation( const LightNode::Unshared &node) const { auto oldChildShadowView = node->previous; auto newChildShadowView = node->current; @@ -1260,7 +1265,7 @@ void LayoutAnimationsProxy::startLayoutAnimation( }); } -void LayoutAnimationsProxy::startSharedTransition( +void LayoutAnimationsProxy_Experimental::startSharedTransition( const int tag, const ShadowView &before, const ShadowView &after, @@ -1298,7 +1303,7 @@ void LayoutAnimationsProxy::startSharedTransition( }); } -void LayoutAnimationsProxy::startProgressTransition( +void LayoutAnimationsProxy_Experimental::startProgressTransition( const int tag, const ShadowView &before, const ShadowView &after, @@ -1325,7 +1330,7 @@ void LayoutAnimationsProxy::startProgressTransition( // MARK: Position Calculation std::vector -LayoutAnimationsProxy::getAbsolutePositionsForRootPathView( +LayoutAnimationsProxy_Experimental::getAbsolutePositionsForRootPathView( const LightNode::Unshared &node) const { std::vector viewsAbsolutePositions; auto currentNode = node; @@ -1356,7 +1361,8 @@ LayoutAnimationsProxy::getAbsolutePositionsForRootPathView( return viewsAbsolutePositions; } -std::optional LayoutAnimationsProxy::parseParentTransforms( +std::optional +LayoutAnimationsProxy_Experimental::parseParentTransforms( const LightNode::Unshared &node, const std::vector &absolutePositions) const { std::vector> transforms; @@ -1426,7 +1432,7 @@ std::optional LayoutAnimationsProxy::parseParentTransforms( // https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.cpp#L548 // We need a copy of these methods to modify the `resolveTransform` method // to accept the transform origin as a parameter instead of as a class field. -react::Transform LayoutAnimationsProxy::resolveTransform( +react::Transform LayoutAnimationsProxy_Experimental::resolveTransform( const LayoutMetrics &layoutMetrics, const Transform &transform, const TransformOrigin &transformOrigin) const { @@ -1462,7 +1468,8 @@ react::Transform LayoutAnimationsProxy::resolveTransform( return transformMatrix; } -std::array LayoutAnimationsProxy::getTranslateForTransformOrigin( +std::array +LayoutAnimationsProxy_Experimental::getTranslateForTransformOrigin( float viewWidth, float viewHeight, const TransformOrigin &transformOrigin) const { @@ -1496,8 +1503,8 @@ std::array LayoutAnimationsProxy::getTranslateForTransformOrigin( * queue where React has already scheduled (but not yet executed) the view * mounting, so the opacity update will execute after the view is mounted. */ -void LayoutAnimationsProxy::restoreOpacityInCaseOfFlakyEnteringAnimation( - SurfaceId surfaceId) const { +void LayoutAnimationsProxy_Experimental:: + restoreOpacityInCaseOfFlakyEnteringAnimation(SurfaceId surfaceId) const { std::vector> opacityToRestore; for (const auto tag : finishedAnimationTags_) { const auto &opacity = layoutAnimations_[tag].opacity; @@ -1544,7 +1551,7 @@ void LayoutAnimationsProxy::restoreOpacityInCaseOfFlakyEnteringAnimation( }); } -const ShadowNode *LayoutAnimationsProxy::findInShadowTreeByTag( +const ShadowNode *LayoutAnimationsProxy_Experimental::findInShadowTreeByTag( const ShadowNode &node, Tag tag) const { if (node.getTag() == tag) { @@ -1559,4 +1566,4 @@ const ShadowNode *LayoutAnimationsProxy::findInShadowTreeByTag( } #endif // ANDROID -} // namespace reanimated +} // namespace reanimated_experimental diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Experimental.h similarity index 97% rename from packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h rename to packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Experimental.h index 13d43ac09369..c237ff66c459 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Experimental.h @@ -20,11 +20,12 @@ #include #include -namespace reanimated { +namespace reanimated_experimental { class ReanimatedModuleProxy; using namespace facebook; +using namespace reanimated; struct LayoutAnimation { ShadowView finalView, currentView, startView; @@ -35,9 +36,9 @@ struct LayoutAnimation { LayoutAnimation &operator=(const LayoutAnimation &other) = default; }; -struct LayoutAnimationsProxy +struct LayoutAnimationsProxy_Experimental : public MountingOverrideDelegate, - public std::enable_shared_from_this { + public std::enable_shared_from_this { mutable std::unordered_map> nodeForTag_; mutable std::unordered_map layoutAnimations_; mutable std::recursive_mutex mutex; @@ -78,7 +79,7 @@ struct LayoutAnimationsProxy std::shared_ptr jsInvoker_; #endif - LayoutAnimationsProxy( + LayoutAnimationsProxy_Experimental( std::shared_ptr layoutAnimationsManager, SharedComponentDescriptorRegistry componentDescriptorRegistry, std::shared_ptr contextContainer, @@ -272,4 +273,4 @@ struct LayoutAnimationsProxy ShadowViewMutationList mutations) const override; }; -} // namespace reanimated +} // namespace reanimated_experimental diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Legacy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Legacy.cpp new file mode 100644 index 000000000000..45b72fb15f4c --- /dev/null +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Legacy.cpp @@ -0,0 +1,1040 @@ +#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_Legacy::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); + +#ifdef ANDROID + restoreOpacityInCaseOfFlakyEnteringAnimation(surfaceId); +#endif // ANDROID + 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_Legacy::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_Legacy::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_Legacy::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_Legacy::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_Legacy::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_Legacy::addOngoingAnimations( + SurfaceId surfaceId, + ShadowViewMutationList &mutations) const { + auto &updateMap = surfaceManager.getUpdateMap(surfaceId); +#ifdef ANDROID + std::vector tagsToUpdate; + for (auto &[tag, updateValues] : updateMap) { + tagsToUpdate.push_back(tag); + } + + auto maybeCorrectedTags = preserveMountedTags_(tagsToUpdate); + if (!maybeCorrectedTags.has_value()) { + return; + } + + auto correctedTags = maybeCorrectedTags->get(); + + // since the map is not updated, we can assume that the ordering of tags in + // correctedTags matches the iterator + int i = -1; +#endif + for (auto &[tag, updateValues] : updateMap) { +#ifdef ANDROID + i++; + if (correctedTags[i] == -1) { + // skip views that have not been mounted yet + // on Android we start entering animations from the JS thread + // so it might happen, that the first frame of the animation goes through + // before the view is first mounted + // https://github.com/software-mansion/react-native-reanimated/issues/7493 + continue; + } +#endif + + auto layoutAnimationIt = layoutAnimations_.find(tag); + + if (layoutAnimationIt == layoutAnimations_.end()) { + continue; + } + + auto &layoutAnimation = layoutAnimationIt->second; + layoutAnimation.isViewAlreadyMounted = true; + 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_Legacy::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_Legacy::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_Legacy::getComponentDescriptorForShadowView( + const ShadowView &shadowView) const { + return componentDescriptorRegistry_->at(shadowView.componentHandle); +} + +bool LayoutAnimationsProxy_Legacy::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_Legacy::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_Legacy::shouldOverridePullTransaction() const { + return true; +} + +void LayoutAnimationsProxy_Legacy::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, {}, false, count}); +} + +void LayoutAnimationsProxy_Legacy::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_Legacy::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_Legacy::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_Legacy::updateOngoingAnimationTarget( + const int tag, + const ShadowViewMutation &mutation) const { + layoutAnimations_[tag].finalView = + std::make_shared(mutation.newChildShadowView); +} + +void LayoutAnimationsProxy_Legacy::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_Legacy::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_Legacy::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_Legacy::maybeRestoreOpacity( + LayoutAnimation &layoutAnimation, + const jsi::Object &newStyle) const { + if (layoutAnimation.opacity && !newStyle.hasProperty(uiRuntime_, "opacity")) { + newStyle.setProperty( + uiRuntime_, "opacity", jsi::Value(*layoutAnimation.opacity)); + if (layoutAnimation.isViewAlreadyMounted) { + // We want to reset opacity only when we are sure that this update will be + // applied to the native view. Otherwise, we want to update opacity using + // the `restoreOpacityInCaseOfFlakyEnteringAnimation` method. + layoutAnimation.opacity.reset(); + } + } +} + +void LayoutAnimationsProxy_Legacy::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); + } +} + +#ifdef ANDROID +/* + * It is possible that we may finish the layout animation before the native view + * is mounted. If the view wasn't mounted, we wouldn't be able to restore the + * opacity of the view. To fix this, we need to schedule a React update that + * will restore the view's opacity. This is safe because we'll use the same + * queue where React has already scheduled (but not yet executed) the view + * mounting, so the opacity update will execute after the view is mounted. + */ +void LayoutAnimationsProxy_Legacy::restoreOpacityInCaseOfFlakyEnteringAnimation( + SurfaceId surfaceId) const { + std::vector> opacityToRestore; + for (const auto tag : finishedAnimationTags_) { + const auto &opacity = layoutAnimations_[tag].opacity; + if (opacity.has_value()) { + opacityToRestore.emplace_back( + std::pair{opacity.value(), tag}); + } + } + if (opacityToRestore.empty()) { + // Animation was successfully finished, and the opacity was restored, so we + // don't need to do anything. Only the Entering animation has a set opacity + // value. + return; + } + const auto weakThis = weak_from_this(); + jsInvoker_->invokeAsync([=](jsi::Runtime &runtime) { + const auto self = weakThis.lock(); + if (!self) { + return; + } + self->uiManager_->getShadowTreeRegistry().visit( + surfaceId, [=](ShadowTree const &shadowTree) { + shadowTree.commit( + [=](RootShadowNode const &oldRootShadowNode) { + const auto self = weakThis.lock(); + if (!self) { + return cloneShadowTreeWithNewProps(oldRootShadowNode, {}); + } + const auto &rootShadowNode = + static_cast(oldRootShadowNode); + PropsMap propsMap; + for (const auto &[opacity, tag] : opacityToRestore) { + const auto *targetShadowNode = + self->findInShadowTreeByTag(rootShadowNode, tag); + if (targetShadowNode != nullptr) { + propsMap[&targetShadowNode->getFamily()].emplace_back( + folly::dynamic::object("opacity", opacity)); + } + } + return cloneShadowTreeWithNewProps(oldRootShadowNode, propsMap); + }, + {}); + }); + }); +} + +const ShadowNode *LayoutAnimationsProxy_Legacy::findInShadowTreeByTag( + const ShadowNode &node, + Tag tag) const { + if (node.getTag() == tag) { + return const_cast(&node); + } + for (auto &child : node.getChildren()) { + if (const auto result = findInShadowTreeByTag(*child, tag)) { + return result; + } + } + return nullptr; +} +#endif // ANDROID + +std::unordered_map &SurfaceManager::getUpdateMap( + SurfaceId surfaceId) { + auto props = props_.find(surfaceId); + if (props != props_.end()) { + return *props->second; + } + + auto newProps = std::make_shared>(); + props_.insert_or_assign(surfaceId, newProps); + return *newProps; +} + +void SurfaceManager::updateWindow( + const SurfaceId surfaceId, + const double windowWidth, + const double windowHeight) { + windows_.insert_or_assign(surfaceId, Rect{windowWidth, windowHeight}); +} + +Rect SurfaceManager::getWindow(SurfaceId surfaceId) { + auto windowIt = windows_.find(surfaceId); + if (windowIt != windows_.end()) { + return windowIt->second; + } + return Rect{0, 0}; +} + +void Node::applyMutationToIndices(ShadowViewMutation mutation) { + const auto parentTag = mutation.parentTag; + if (tag != parentTag) { + return; + } + + int delta = mutation.type == ShadowViewMutation::Insert ? 1 : -1; + for (int i = children.size() - 1; i >= 0; i--) { + if (children[i]->mutation.index < mutation.index) { + return; + } + children[i]->mutation.index += delta; + } +} + +// Should only be called on unflattened parents +void Node::removeChildFromUnflattenedTree(std::shared_ptr child) { + for (int i = unflattenedChildren.size() - 1; i >= 0; i--) { + if (unflattenedChildren[i]->tag == child->tag) { + unflattenedChildren.erase(unflattenedChildren.begin() + i); + break; + } + } + + auto &flattenedChildren = child->parent->children; + for (int i = flattenedChildren.size() - 1; i >= 0; i--) { + if (flattenedChildren[i]->tag == child->tag) { + flattenedChildren.erase(flattenedChildren.begin() + i); + return; + } + flattenedChildren[i]->mutation.index--; + } +} + +void Node::insertChildren( + std::vector> &newChildren) { + mergeAndSwap(children, newChildren); +} + +void Node::insertUnflattenedChildren( + std::vector> &newChildren) { + mergeAndSwap(unflattenedChildren, newChildren); +} + +bool Node::isMutationMode() { + return false; +} + +bool MutationNode::isMutationMode() { + return true; +} + +} // namespace reanimated diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Legacy.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Legacy.h new file mode 100644 index 000000000000..9cc1f164d0e9 --- /dev/null +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Legacy.h @@ -0,0 +1,331 @@ +#pragma once + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace reanimated { + +class ReanimatedModuleProxy; + +using namespace facebook; + +#pragma once + +struct Rect { + double width, height; +}; + +struct Frame { + std::optional x, y, width, height; + Frame(jsi::Runtime &runtime, const jsi::Object &newStyle) { + if (newStyle.hasProperty(runtime, "originX")) { + x = newStyle.getProperty(runtime, "originX").asNumber(); + } + if (newStyle.hasProperty(runtime, "originY")) { + y = newStyle.getProperty(runtime, "originY").asNumber(); + } + if (newStyle.hasProperty(runtime, "width")) { + width = newStyle.getProperty(runtime, "width").asNumber(); + } + if (newStyle.hasProperty(runtime, "height")) { + height = newStyle.getProperty(runtime, "height").asNumber(); + } + } +}; + +struct UpdateValues { + Props::Shared newProps; + Frame frame; +}; + +struct Snapshot { + double x, y, width, height, windowWidth, windowHeight; + Snapshot(const ShadowView &shadowView, Rect window) { + const auto &frame = shadowView.layoutMetrics.frame; + x = frame.origin.x; + y = frame.origin.y; + width = frame.size.width; + height = frame.size.height; + windowWidth = window.width; + windowHeight = window.height; + } +}; + +typedef enum ExitingState { + UNDEFINED = 1, + WAITING = 2, + ANIMATING = 4, + DEAD = 8, + MOVED = 16, + DELETED = 32, +} ExitingState; + +struct MutationNode; + +/** + Represents a view that was either removed or had a child removed from the + ShadowTree + */ +struct Node { + std::vector> children, unflattenedChildren; + std::shared_ptr parent, unflattenedParent; + Tag tag; + void removeChildFromUnflattenedTree(std::shared_ptr child); + void applyMutationToIndices(ShadowViewMutation mutation); + void insertChildren(std::vector> &newChildren); + void insertUnflattenedChildren( + std::vector> &newChildren); + virtual bool isMutationMode(); + explicit Node(const Tag tag) : tag(tag) {} + Node(Node &&node) + : children(std::move(node.children)), + unflattenedChildren(std::move(node.unflattenedChildren)), + tag(node.tag) {} + Node(Node &node) + : children(node.children), + unflattenedChildren(node.unflattenedChildren), + tag(node.tag) {} + virtual ~Node() = default; +}; + +/** + Represents a view that was removed from the ShadowTree + */ +struct MutationNode : public Node { + ShadowViewMutation mutation; + ExitingState state = UNDEFINED; + explicit MutationNode(ShadowViewMutation &mutation) + : Node(mutation.oldChildShadowView.tag), mutation(mutation) {} + MutationNode(ShadowViewMutation &mutation, Node &&node) + : Node(std::move(node)), mutation(mutation) {} + bool isMutationMode() override; +}; + +struct SurfaceManager { + mutable std::unordered_map< + SurfaceId, + std::shared_ptr>> + props_; + mutable std::unordered_map windows_; + + std::unordered_map &getUpdateMap(SurfaceId surfaceId); + void + updateWindow(SurfaceId surfaceId, double windowWidth, double windowHeight); + Rect getWindow(SurfaceId surfaceId); +}; + +static inline void updateLayoutMetrics( + LayoutMetrics &layoutMetrics, + Frame &frame) { + // we use optional's here to avoid overwriting non-animated values + if (frame.width) { + layoutMetrics.frame.size.width = *frame.width; + } + if (frame.height) { + layoutMetrics.frame.size.height = *frame.height; + } + if (frame.x) { + layoutMetrics.frame.origin.x = *frame.x; + } + if (frame.y) { + layoutMetrics.frame.origin.y = *frame.y; + } +} + +static inline bool isRNSScreen(std::shared_ptr node) { + const auto &componentName = node->mutation.oldChildShadowView.componentName; + return !std::strcmp(componentName, "RNSScreenStack") || + !std::strcmp(componentName, "RNSScreen") || + !std::strcmp(componentName, "RNSModalScreen"); +} + +static inline bool hasLayoutChanged(const ShadowViewMutation &mutation) { + return mutation.oldChildShadowView.layoutMetrics.frame != + mutation.newChildShadowView.layoutMetrics.frame; +} + +static inline void mergeAndSwap( + std::vector> &A, + std::vector> &B) { + std::vector> merged; + auto it1 = A.begin(), it2 = B.begin(); + while (it1 != A.end() && it2 != B.end()) { + if ((*it1)->mutation.index < (*it2)->mutation.index) { + merged.push_back(*it1); + it1++; + } else { + merged.push_back(*it2); + it2++; + } + } + while (it1 != A.end()) { + merged.push_back(*it1); + it1++; + } + while (it2 != B.end()) { + merged.push_back(*it2); + it2++; + } + std::swap(A, merged); +} + +struct LayoutAnimation { + std::shared_ptr finalView, currentView; + Tag parentTag; + std::optional opacity; + bool isViewAlreadyMounted = false; + int count = 1; + LayoutAnimation &operator=(const LayoutAnimation &other) = default; +}; + +struct LayoutAnimationsProxy_Legacy + : public MountingOverrideDelegate, + public std::enable_shared_from_this { + mutable std::unordered_map> nodeForTag_; + mutable std::unordered_map layoutAnimations_; + mutable std::recursive_mutex mutex; + mutable SurfaceManager surfaceManager; + mutable std::unordered_set> deadNodes; + mutable std::unordered_map leastRemoved; + mutable std::vector finishedAnimationTags_; + std::shared_ptr layoutAnimationsManager_; + std::shared_ptr contextContainer_; + SharedComponentDescriptorRegistry componentDescriptorRegistry_; + jsi::Runtime &uiRuntime_; + const std::shared_ptr uiScheduler_; + PreserveMountedTagsFunction preserveMountedTags_; +#ifdef ANDROID + std::shared_ptr uiManager_; + std::shared_ptr jsInvoker_; +#endif + + LayoutAnimationsProxy_Legacy( + std::shared_ptr layoutAnimationsManager, + SharedComponentDescriptorRegistry componentDescriptorRegistry, + std::shared_ptr contextContainer, + jsi::Runtime &uiRuntime, + const std::shared_ptr uiScheduler +#ifdef ANDROID + , + PreserveMountedTagsFunction filterUnmountedTagsFunction, + std::shared_ptr uiManager, + std::shared_ptr jsInvoker +#endif + ) + : layoutAnimationsManager_(layoutAnimationsManager), + contextContainer_(contextContainer), + componentDescriptorRegistry_(componentDescriptorRegistry), + uiRuntime_(uiRuntime), + uiScheduler_(uiScheduler) +#ifdef ANDROID + , + preserveMountedTags_(filterUnmountedTagsFunction), + uiManager_(uiManager), + jsInvoker_(jsInvoker) +#endif // ANDROID + { + } + + void startEnteringAnimation(const int tag, ShadowViewMutation &mutation) + const; + void startExitingAnimation(const int tag, ShadowViewMutation &mutation) const; + void startLayoutAnimation(const int tag, const ShadowViewMutation &mutation) + const; + + void transferConfigFromNativeID(const std::string nativeId, const int tag) + const; + std::optional progressLayoutAnimation( + int tag, + const jsi::Object &newStyle); + std::optional endLayoutAnimation(int tag, bool shouldRemove); + void maybeCancelAnimation(const int tag) const; + + void parseRemoveMutations( + std::unordered_map &movedViews, + ShadowViewMutationList &mutations, + std::vector> &roots) const; + void handleRemovals( + ShadowViewMutationList &filteredMutations, + std::vector> &roots) const; + + void handleUpdatesAndEnterings( + ShadowViewMutationList &filteredMutations, + const std::unordered_map &movedViews, + ShadowViewMutationList &mutations, + const PropsParserContext &propsParserContext, + SurfaceId surfaceId) const; + void addOngoingAnimations( + SurfaceId surfaceId, + ShadowViewMutationList &mutations) const; + void updateOngoingAnimationTarget( + const int tag, + const ShadowViewMutation &mutation) const; + std::shared_ptr cloneViewWithoutOpacity( + facebook::react::ShadowViewMutation &mutation, + const PropsParserContext &propsParserContext) const; + void maybeRestoreOpacity( + LayoutAnimation &layoutAnimation, + const jsi::Object &newStyle) const; + void maybeUpdateWindowDimensions( + facebook::react::ShadowViewMutation &mutation, + SurfaceId surfaceId) const; + void createLayoutAnimation( + const ShadowViewMutation &mutation, + ShadowView &oldView, + const SurfaceId &surfaceId, + const int tag) const; + + void updateIndexForMutation(ShadowViewMutation &mutation) const; + + void removeRecursively( + std::shared_ptr node, + ShadowViewMutationList &mutations) const; + bool startAnimationsRecursively( + std::shared_ptr node, + const bool shouldRemoveSubviewsWithoutAnimations, + const bool shouldAnimate, + const bool isScreenPop, + ShadowViewMutationList &mutations) const; + void endAnimationsRecursively( + std::shared_ptr node, + ShadowViewMutationList &mutations) const; + void maybeDropAncestors( + std::shared_ptr node, + std::shared_ptr child, + ShadowViewMutationList &cleanupMutations) const; + + const ComponentDescriptor &getComponentDescriptorForShadowView( + const ShadowView &shadowView) const; +#ifdef ANDROID + void restoreOpacityInCaseOfFlakyEnteringAnimation(SurfaceId surfaceId) const; + const ShadowNode *findInShadowTreeByTag(const ShadowNode &node, Tag tag) + const; +#endif // ANDROID + // MountingOverrideDelegate + + bool shouldOverridePullTransaction() const override; + std::optional pullTransaction( + SurfaceId surfaceId, + MountingTransaction::Number number, + const TransactionTelemetry &telemetry, + ShadowViewMutationList mutations) const override; +}; + +} // namespace reanimated diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsUtils.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsUtils.cpp index 3333653ddbeb..2d062c250542 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsUtils.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsUtils.cpp @@ -1,6 +1,6 @@ #include -namespace reanimated { +namespace reanimated_experimental { std::unordered_map &SurfaceManager::getUpdateMap( SurfaceId surfaceId) { @@ -80,4 +80,4 @@ bool Node::isMutationMode() { bool MutationNode::isMutationMode() { return true; } -} // namespace reanimated +} // namespace reanimated_experimental diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsUtils.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsUtils.h index 58c2d8aedca0..386e467ea67b 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsUtils.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsUtils.h @@ -11,7 +11,7 @@ #include #include -namespace reanimated { +namespace reanimated_experimental { struct Rect { double width, height; @@ -211,4 +211,4 @@ static inline void mergeAndSwap( std::swap(A, merged); } -} // namespace reanimated +} // namespace reanimated_experimental diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp index b9561fdbc077..1b23d0d04d18 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp @@ -15,6 +15,8 @@ #include #include +#include +#include #include #include @@ -143,10 +145,16 @@ void ReanimatedModuleProxy::init( if (!strongThis) { return; } - - auto surfaceId = - strongThis->layoutAnimationsProxy_->progressLayoutAnimation( - tag, newStyle); + std::optional surfaceId; + if constexpr (StaticFeatureFlags::getFlag( + "SHARED_ELEMENT_TRANSITIONS")) { + surfaceId = strongThis->layoutAnimationsProxyExperimental_ + ->progressLayoutAnimation(tag, newStyle); + } else { + surfaceId = surfaceId = + strongThis->layoutAnimationsProxyLegacy_->progressLayoutAnimation( + tag, newStyle); + } if (!surfaceId) { return; } @@ -169,8 +177,16 @@ void ReanimatedModuleProxy::init( return; } - auto surfaceId = strongThis->layoutAnimationsProxy_->endLayoutAnimation( - tag, shouldRemove); + std::optional surfaceId; + if constexpr (StaticFeatureFlags::getFlag( + "SHARED_ELEMENT_TRANSITIONS")) { + surfaceId = strongThis->layoutAnimationsProxyExperimental_ + ->endLayoutAnimation(tag, shouldRemove); + } else { + surfaceId = + strongThis->layoutAnimationsProxyLegacy_->endLayoutAnimation( + tag, shouldRemove); + } if (!strongThis->layoutAnimationRenderRequested_) { strongThis->layoutAnimationRenderRequested_ = true; @@ -594,38 +610,40 @@ bool ReanimatedModuleProxy::handleRawEvent( eventType = "on" + eventType.substr(3); } - if (!strcmp(eventType.c_str(), "onTransitionProgress")) { - jsi::Runtime &rt = - workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(); - const auto &eventPayload = rawEvent.eventPayload; - jsi::Object payload = eventPayload->asJSIValue(rt).asObject(rt); - auto progress = payload.getProperty(rt, "progress").asNumber(); - auto closing = payload.getProperty(rt, "closing").asNumber(); - auto goingForward = payload.getProperty(rt, "goingForward").asNumber(); - auto swiping = payload.getProperty(rt, "swiping").asNumber(); - - auto surfaceId = layoutAnimationsProxy_->onTransitionProgress( - tag, progress, closing, goingForward, swiping); - if (!surfaceId) { + if constexpr (StaticFeatureFlags::getFlag("SHARED_ELEMENT_TRANSITIONS")) { + if (!strcmp(eventType.c_str(), "onTransitionProgress")) { + jsi::Runtime &rt = + workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(); + const auto &eventPayload = rawEvent.eventPayload; + jsi::Object payload = eventPayload->asJSIValue(rt).asObject(rt); + auto progress = payload.getProperty(rt, "progress").asNumber(); + auto closing = payload.getProperty(rt, "closing").asNumber(); + auto goingForward = payload.getProperty(rt, "goingForward").asNumber(); + auto swiping = payload.getProperty(rt, "swiping").asNumber(); + + auto surfaceId = layoutAnimationsProxyExperimental_->onTransitionProgress( + tag, progress, closing, goingForward, swiping); + if (!surfaceId) { + return false; + } + // TODO: enumerate -> visit + uiManager_->getShadowTreeRegistry().enumerate( + [](const ShadowTree &shadowTree, bool &) { + shadowTree.notifyDelegatesOfUpdates(); + }); return false; - } - // TODO: enumerate -> visit - uiManager_->getShadowTreeRegistry().enumerate( - [](const ShadowTree &shadowTree, bool &) { - shadowTree.notifyDelegatesOfUpdates(); - }); - return false; - } else if (!strcmp(eventType.c_str(), "onGestureCancel")) { - auto surfaceId = layoutAnimationsProxy_->onGestureCancel(); - if (!surfaceId) { + } else if (!strcmp(eventType.c_str(), "onGestureCancel")) { + auto surfaceId = layoutAnimationsProxyExperimental_->onGestureCancel(); + if (!surfaceId) { + return false; + } + // TODO: enumerate -> visit + uiManager_->getShadowTreeRegistry().enumerate( + [](const ShadowTree &shadowTree, bool &) { + shadowTree.notifyDelegatesOfUpdates(); + }); return false; } - // TODO: enumerate -> visit - uiManager_->getShadowTreeRegistry().enumerate( - [](const ShadowTree &shadowTree, bool &) { - shadowTree.notifyDelegatesOfUpdates(); - }); - return false; } if (!isAnyHandlerWaitingForEvent(eventType, tag)) { @@ -1400,7 +1418,10 @@ void ReanimatedModuleProxy::initializeFabric( mountHook_ = std::make_shared( uiManager_, updatesRegistryManager_, request); commitHook_ = std::make_shared( - uiManager_, updatesRegistryManager_, layoutAnimationsProxy_); + uiManager_, + updatesRegistryManager_, + layoutAnimationsProxyLegacy_, + layoutAnimationsProxyExperimental_); } void ReanimatedModuleProxy::initializeLayoutAnimationsProxy() { @@ -1413,23 +1434,41 @@ void ReanimatedModuleProxy::initializeLayoutAnimationsProxy() { .lock(); if (componentDescriptorRegistry) { - layoutAnimationsProxy_ = std::make_shared( - layoutAnimationsManager_, - componentDescriptorRegistry, - scheduler->getContextContainer(), - workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(), - workletsModuleProxy_->getUIScheduler() + if constexpr (StaticFeatureFlags::getFlag("SHARED_ELEMENT_TRANSITIONS")) { + layoutAnimationsProxyExperimental_ = std::make_shared< + reanimated_experimental::LayoutAnimationsProxy_Experimental>( + layoutAnimationsManager_, + componentDescriptorRegistry, + scheduler->getContextContainer(), + workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(), + workletsModuleProxy_->getUIScheduler() #ifdef ANDROID - , - filterUnmountedTagsFunction_, - uiManager_, - jsInvoker_ + , + filterUnmountedTagsFunction_, + uiManager_, + jsInvoker_ #endif - ); + ); #ifdef __APPLE__ - layoutAnimationsProxy_->setForceScreenSnapshotFunction( - forceScreenSnapshot_); + layoutAnimationsProxyExperimental_->setForceScreenSnapshotFunction( + forceScreenSnapshot_); #endif + } else { + layoutAnimationsProxyLegacy_ = + std::make_shared( + layoutAnimationsManager_, + componentDescriptorRegistry, + scheduler->getContextContainer(), + workletsModuleProxy_->getUIWorkletRuntime()->getJSIRuntime(), + workletsModuleProxy_->getUIScheduler() +#ifdef ANDROID + , + filterUnmountedTagsFunction_, + uiManager_, + jsInvoker_ +#endif + ); + } } } diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h index ff6cdf1d2551..c50a5f62a5d0 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h @@ -15,7 +15,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -251,7 +252,9 @@ class ReanimatedModuleProxy const PreserveMountedTagsFunction filterUnmountedTagsFunction_; std::shared_ptr uiManager_; - std::shared_ptr layoutAnimationsProxy_; + std::shared_ptr layoutAnimationsProxyLegacy_; + std::shared_ptr + layoutAnimationsProxyExperimental_; std::shared_ptr commitHook_; std::shared_ptr mountHook_; std::set layoutAnimationFlushRequests_; diff --git a/packages/react-native-reanimated/apple/reanimated/apple/REANodesManager.h b/packages/react-native-reanimated/apple/reanimated/apple/REANodesManager.h index 9477488e6eb7..88eed8e90e50 100644 --- a/packages/react-native-reanimated/apple/reanimated/apple/REANodesManager.h +++ b/packages/react-native-reanimated/apple/reanimated/apple/REANodesManager.h @@ -1,8 +1,8 @@ #import #import -#import #import +#import typedef void (^REAOnAnimationCallback)(READisplayLink *displayLink); typedef void (^REAEventHandler)(id event); diff --git a/packages/react-native-reanimated/src/featureFlags/staticFlags.json b/packages/react-native-reanimated/src/featureFlags/staticFlags.json index 77469aa27d6a..a9d862f1083f 100644 --- a/packages/react-native-reanimated/src/featureFlags/staticFlags.json +++ b/packages/react-native-reanimated/src/featureFlags/staticFlags.json @@ -5,5 +5,6 @@ "IOS_SYNCHRONOUSLY_UPDATE_UI_PROPS": false, "EXPERIMENTAL_CSS_ANIMATIONS_FOR_SVG_COMPONENTS": false, "USE_SYNCHRONIZABLE_FOR_MUTABLES": false, - "USE_COMMIT_HOOK_ONLY_FOR_REACT_COMMITS": false + "USE_COMMIT_HOOK_ONLY_FOR_REACT_COMMITS": false, + "SHARED_ELEMENT_TRANSITIONS": false } From 27a060399659b8a494dcf5a2183ec811af310499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Fri, 17 Oct 2025 11:49:44 +0200 Subject: [PATCH 67/90] couldnt build release --- .../Common/cpp/reanimated/CSS/common/transforms/Quaternion.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/common/transforms/Quaternion.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/common/transforms/Quaternion.cpp index cddca3209965..338d26c81459 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/common/transforms/Quaternion.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/common/transforms/Quaternion.cpp @@ -1,4 +1,5 @@ #include +#include namespace reanimated::css { From f830249d53f7d7df3fc8e45e62eebc5798422984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Fri, 17 Oct 2025 13:37:12 +0200 Subject: [PATCH 68/90] bump screens --- apps/common-app/package.json | 3 +-- .../cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/common-app/package.json b/apps/common-app/package.json index 7050591c0208..1593ee9eb002 100644 --- a/apps/common-app/package.json +++ b/apps/common-app/package.json @@ -36,8 +36,7 @@ "react-native-pager-view": "7.0.0", "react-native-reanimated": "workspace:*", "react-native-safe-area-context": "5.6.0", - "react-native-screens": "patch:react-native-screens@npm%3A4.14.1#~/.yarn/patches/react-native-screens-npm-4.14.1-b511d342ea.patch", - "react-native-svg": "15.12.1", + "react-native-screens": "^4.17.1", "react-native-worklets": "workspace:*", "react-strict-dom": "0.0.27" }, diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp index 1b23d0d04d18..03809c21883a 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp @@ -619,10 +619,10 @@ bool ReanimatedModuleProxy::handleRawEvent( auto progress = payload.getProperty(rt, "progress").asNumber(); auto closing = payload.getProperty(rt, "closing").asNumber(); auto goingForward = payload.getProperty(rt, "goingForward").asNumber(); - auto swiping = payload.getProperty(rt, "swiping").asNumber(); +// auto swiping = payload.getProperty(rt, "swiping").asNumber(); auto surfaceId = layoutAnimationsProxyExperimental_->onTransitionProgress( - tag, progress, closing, goingForward, swiping); + tag, progress, closing, goingForward, false); if (!surfaceId) { return false; } From 9083d6275d3f24116ddea3648165002df3dfb68b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Fri, 31 Oct 2025 14:32:00 +0100 Subject: [PATCH 69/90] Better nativeId parsing --- .../LayoutAnimationsProxy_Experimental.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Experimental.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Experimental.cpp index cda2c5e79fa0..79a074964709 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Experimental.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Experimental.cpp @@ -1020,15 +1020,19 @@ void LayoutAnimationsProxy_Experimental::maybeCancelAnimation( void LayoutAnimationsProxy_Experimental::transferConfigFromNativeID( const std::string nativeIdString, const int tag) const { - if (nativeIdString.empty()) { + if (nativeIdString.empty() || nativeIdString.length() > 9) { return; } - try { - auto nativeId = stoi(nativeIdString); - layoutAnimationsManager_->transferConfigFromNativeID(nativeId, tag); - } catch (std::invalid_argument) { - } catch (std::out_of_range) { + auto nativeId = 0; + for (int i = 0; i < nativeIdString.length(); i++) { + if (nativeIdString[i] < '0' || nativeIdString[i] > '9') { + return; + } + nativeId *= 10; + nativeId += nativeIdString[i] - '0'; } + + layoutAnimationsManager_->transferConfigFromNativeID(nativeId, tag); } // When entering animations start, we temporarily set opacity to 0 From cf6f06ade7f04e748624365007f2b6b222105024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Fri, 31 Oct 2025 16:57:25 +0100 Subject: [PATCH 70/90] Add dynamic set configuration --- .../LayoutAnimations/LayoutAnimationType.h | 1 + .../LayoutAnimationsManager.cpp | 7 ++- .../src/commonTypes.ts | 4 +- .../AnimatedComponent.tsx | 53 +++++++++++-------- .../createAnimatedComponent/commonTypes.ts | 2 + .../src/featureFlags/index.ts | 1 + .../src/helperTypes.ts | 8 ++- .../src/layoutReanimation/SharedTransition.ts | 6 ++- 8 files changed, 56 insertions(+), 26 deletions(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationType.h b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationType.h index 8da069b27575..4df1bd44c42a 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationType.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationType.h @@ -5,4 +5,5 @@ typedef enum LayoutAnimationType { EXITING = 2, LAYOUT = 3, SHARED_ELEMENT_TRANSITION = 4, + SHARED_ELEMENT_TRANSITION_NATIVE_ID = 5, } LayoutAnimationType; diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.cpp index 57877eba13f7..6c1f6d1bd21d 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.cpp @@ -15,11 +15,16 @@ void LayoutAnimationsManager::configureAnimationBatch( enteringAnimationsForNativeID_[tag] = config; continue; } - if (type == SHARED_ELEMENT_TRANSITION) { + if (type == SHARED_ELEMENT_TRANSITION_NATIVE_ID) { sharedTransitionsForNativeID_[tag] = config; sharedTransitionManager_->nativeIDToName_[tag] = sharedTag; continue; } + if (type == SHARED_ELEMENT_TRANSITION) { + sharedTransitions_[tag] = config; + sharedTransitionManager_->tagToName_[tag] = sharedTag; + continue; + } if (config == nullptr) { getConfigsForType(type).erase(tag); } else { diff --git a/packages/react-native-reanimated/src/commonTypes.ts b/packages/react-native-reanimated/src/commonTypes.ts index 937e19a21ffd..1a07c01cb02d 100644 --- a/packages/react-native-reanimated/src/commonTypes.ts +++ b/packages/react-native-reanimated/src/commonTypes.ts @@ -95,7 +95,9 @@ export enum LayoutAnimationType { EXITING = 2, LAYOUT = 3, SHARED_ELEMENT_TRANSITION = 4, - SHARED_ELEMENT_TRANSITION_PROGRESS = 5, + SHARED_ELEMENT_TRANSITION_NATIVE_ID = 5, + SHARED_ELEMENT_TRANSITION_PROGRESS = 6, + SHARED_ELEMENT_TRANSITION_PROGRESS_NATIVE_ID = 7, } export type LayoutAnimationFunction = ( diff --git a/packages/react-native-reanimated/src/createAnimatedComponent/AnimatedComponent.tsx b/packages/react-native-reanimated/src/createAnimatedComponent/AnimatedComponent.tsx index de8c61caece7..8d494d21ca20 100644 --- a/packages/react-native-reanimated/src/createAnimatedComponent/AnimatedComponent.tsx +++ b/packages/react-native-reanimated/src/createAnimatedComponent/AnimatedComponent.tsx @@ -9,8 +9,10 @@ import type { StyleProps } from '../commonTypes'; import { LayoutAnimationType } from '../commonTypes'; import { SkipEnteringContext } from '../component/LayoutAnimationConfig'; import ReanimatedAnimatedComponent from '../css/component/AnimatedComponent'; +import { getStaticFeatureFlag } from '../featureFlags'; import type { AnimatedStyleHandle } from '../hook/commonTypes'; -import type { BaseAnimationBuilder } from '../layoutReanimation'; +import { type BaseAnimationBuilder } from '../layoutReanimation'; +import { SharedTransition } from '../layoutReanimation/SharedTransition'; import { configureWebLayoutAnimations, getReducedMotionFromConfig, @@ -38,7 +40,6 @@ import jsPropsUpdater from './JSPropsUpdater'; import { NativeEventsManager } from './NativeEventsManager'; import { PropsFilter } from './PropsFilter'; import { filterStyles, flattenArray } from './utils'; -import { SharedTransition } from '../layoutReanimation/SharedTransition'; let id = 0; @@ -73,6 +74,7 @@ export default class AnimatedComponent static contextType = SkipEnteringContext; context!: React.ContextType; reanimatedID = id++; + _sharedTransition?: SharedTransition; constructor( ChildComponent: AnyComponent, @@ -88,29 +90,11 @@ export default class AnimatedComponent this.jestAnimatedStyle = { value: {} }; this.jestAnimatedProps = { value: {} }; } - - if (this.props.sharedTransitionTag) { - console.log("'Shared transition tag: ", this.props.sharedTransitionTag); - updateLayoutAnimations( - this.reanimatedID, - LayoutAnimationType.SHARED_ELEMENT_TRANSITION, - maybeBuild( - SharedTransition.duration(500), - this.props?.style, - this._displayName - ), - undefined, - this.props.sharedTransitionTag - ); - } - + this._configureSharedTransition(true); const entering = this.props.entering; const skipEntering = this.context?.current; if (!skipEntering) { - this._configureLayoutAnimation( - LayoutAnimationType.ENTERING, - this.props.entering - ); + this._configureLayoutAnimation(LayoutAnimationType.ENTERING, entering); } } @@ -309,6 +293,7 @@ export default class AnimatedComponent this.props.exiting, prevProps.exiting ); + this._configureSharedTransition(); this._NativeEventsManager?.updateEvents(prevProps); this._updateAnimatedStylesAndProps(); @@ -391,6 +376,30 @@ export default class AnimatedComponent ); } + _configureSharedTransition(useNativeId?: boolean) { + if (!getStaticFeatureFlag('SHARED_ELEMENT_TRANSITIONS')) { + return; + } + if (this.props.sharedTransitionTag) { + const sharedTransition = + this.props.sharedTransitionStyle ?? + this._sharedTransition ?? + new SharedTransition(); + if (this._sharedTransition !== sharedTransition) { + updateLayoutAnimations( + useNativeId ? this.reanimatedID : this.getComponentViewTag(), + useNativeId + ? LayoutAnimationType.SHARED_ELEMENT_TRANSITION_NATIVE_ID + : LayoutAnimationType.SHARED_ELEMENT_TRANSITION, + maybeBuild(sharedTransition, this.props?.style, this._displayName), + undefined, + this.props.sharedTransitionTag + ); + this._sharedTransition = sharedTransition; + } + } + } + // _onSetLocalRef() { // const tag = this.getComponentViewTag(); diff --git a/packages/react-native-reanimated/src/createAnimatedComponent/commonTypes.ts b/packages/react-native-reanimated/src/createAnimatedComponent/commonTypes.ts index dfa9036ef2bb..da57ce314956 100644 --- a/packages/react-native-reanimated/src/createAnimatedComponent/commonTypes.ts +++ b/packages/react-native-reanimated/src/createAnimatedComponent/commonTypes.ts @@ -12,6 +12,7 @@ import type { } from '../commonTypes'; import type { SkipEnteringContext } from '../component/LayoutAnimationConfig'; import type { BaseAnimationBuilder } from '../layoutReanimation'; +import type { SharedTransition } from '../layoutReanimation/SharedTransition'; import type { ViewDescriptorsSet } from '../ViewDescriptorsSet'; export interface AnimatedProps extends Record { @@ -85,6 +86,7 @@ export type AnimatedComponentProps< jestAnimatedValues?: RefObject; animatedStyle?: StyleProps; sharedTransitionTag?: string; + sharedTransitionStyle?: SharedTransition & LayoutAnimationStaticContext; layout?: ( | BaseAnimationBuilder | ILayoutAnimationBuilder diff --git a/packages/react-native-reanimated/src/featureFlags/index.ts b/packages/react-native-reanimated/src/featureFlags/index.ts index b5246849e8f4..73cfde266710 100644 --- a/packages/react-native-reanimated/src/featureFlags/index.ts +++ b/packages/react-native-reanimated/src/featureFlags/index.ts @@ -59,6 +59,7 @@ const DefaultStaticFeatureFlags = { EXPERIMENTAL_CSS_ANIMATIONS_FOR_SVG_COMPONENTS: false, USE_SYNCHRONIZABLE_FOR_MUTABLES: false, USE_COMMIT_HOOK_ONLY_FOR_REACT_COMMITS: false, + SHARED_ELEMENT_TRANSITIONS: false, } as const satisfies typeof StaticFeatureFlagsJSON; type StaticFeatureFlagsSchema = { diff --git a/packages/react-native-reanimated/src/helperTypes.ts b/packages/react-native-reanimated/src/helperTypes.ts index 2af154ed671a..b29c7abafc83 100644 --- a/packages/react-native-reanimated/src/helperTypes.ts +++ b/packages/react-native-reanimated/src/helperTypes.ts @@ -19,6 +19,7 @@ import type { CSSStyle } from './css'; import type { AddArrayPropertyType } from './css/types'; import type { BaseAnimationBuilder } from './layoutReanimation/animationBuilder/BaseAnimationBuilder'; import type { ReanimatedKeyframe } from './layoutReanimation/animationBuilder/Keyframe'; +import type { SharedTransition } from './layoutReanimation/SharedTransition'; export type EntryOrExitLayoutType = | BaseAnimationBuilder @@ -88,6 +89,11 @@ type LayoutProps = { exiting?: EntryOrExitLayoutType; }; +type SharedTransitionProps = { + sharedTransitionTag?: string; + sharedTransitionStyle?: SharedTransition; +}; + type AnimatedPropsProp = RestProps & AnimatedStyleProps & LayoutProps; @@ -103,6 +109,6 @@ export type AnimatedProps = RestProps & animatedProps?: AddArrayPropertyType< Partial> | CSSStyle >; - }; + } & SharedTransitionProps; // THE LAND OF THE DEPRECATED diff --git a/packages/react-native-reanimated/src/layoutReanimation/SharedTransition.ts b/packages/react-native-reanimated/src/layoutReanimation/SharedTransition.ts index f852c1ae2be7..e22b31e422b9 100644 --- a/packages/react-native-reanimated/src/layoutReanimation/SharedTransition.ts +++ b/packages/react-native-reanimated/src/layoutReanimation/SharedTransition.ts @@ -1,4 +1,5 @@ 'use strict'; +import { logger } from '../common'; import type { ILayoutAnimationBuilder, LayoutAnimationFunction, @@ -20,6 +21,9 @@ export class SharedTransition build = (): LayoutAnimationFunction => { const delayFunction = this.getDelayFunction(); + if (!this.durationV) { + this.durationV = 500; + } const [animation, config] = this.getAnimationAndConfig(); const callback = this.callbackV; const delay = this.getDelay(); @@ -62,7 +66,7 @@ export class SharedTransition } else if (key === 'transformOrigin') { animations[key] = target.map(animationFactory); } else { - console.error('Unexpected array in SharedTransition:', key); + logger.error(`Unexpected array in SharedTransition: ${key}`); } } else { animations[key] = animationFactory(values.target[key]); From f9f1940205e347774bd617d9c25cdbb3892ec354 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Fri, 31 Oct 2025 17:09:07 +0100 Subject: [PATCH 71/90] many screens works! --- apps/common-app/src/apps/reanimated/examples/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/common-app/src/apps/reanimated/examples/index.ts b/apps/common-app/src/apps/reanimated/examples/index.ts index e2f62ff2b1b4..c8c41172ddaa 100644 --- a/apps/common-app/src/apps/reanimated/examples/index.ts +++ b/apps/common-app/src/apps/reanimated/examples/index.ts @@ -882,7 +882,7 @@ export const EXAMPLES: Record = { title: '[SET] Many screens', screen: ManyScreensExample, shouldWork: { - ios: false, // header height issues on iOS 26 + ios: true, android: true, }, }, From ef600f23abfa7bd76f5303b1ce713ca1c4431a14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Fri, 31 Oct 2025 17:32:37 +0100 Subject: [PATCH 72/90] export SharedTransition --- .../SharedElementTransitions/BorderRadii.tsx | 8 +- .../SharedElementTransitions/ChangeTheme.tsx | 1 - .../CustomTransition.tsx | 1 - .../ProgressTransition.tsx | 16 +- .../ReducedMotionSharedExample.tsx | 182 +++++++++--------- .../TransitionRestart.tsx | 5 +- packages/react-native-reanimated/src/index.ts | 1 + .../src/layoutReanimation/index.ts | 1 + 8 files changed, 104 insertions(+), 111 deletions(-) diff --git a/apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/BorderRadii.tsx b/apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/BorderRadii.tsx index e8d9e92bbe01..8c1404c10aad 100644 --- a/apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/BorderRadii.tsx +++ b/apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/BorderRadii.tsx @@ -3,15 +3,11 @@ import type { NativeStackScreenProps } from '@react-navigation/native-stack'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; import * as React from 'react'; import { Button, StyleSheet, View } from 'react-native'; -import Animated, { - SharedTransition, - SharedTransitionType, -} from 'react-native-reanimated'; +import Animated, { SharedTransition } from 'react-native-reanimated'; +import photo from './assets/image.jpg'; const Stack = createNativeStackNavigator(); -const photo = require('./assets/image.jpg'); - const transition = undefined; // SharedTransition.duration(1000).defaultTransitionType( diff --git a/apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/ChangeTheme.tsx b/apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/ChangeTheme.tsx index cba1880d875a..984f05785ac4 100644 --- a/apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/ChangeTheme.tsx +++ b/apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/ChangeTheme.tsx @@ -13,7 +13,6 @@ import { Button, StyleSheet, Text, View } from 'react-native'; import Animated, { Layout, SharedTransition, - SharedTransitionType, withSpring, } from 'react-native-reanimated'; diff --git a/apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/CustomTransition.tsx b/apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/CustomTransition.tsx index 4edfe9873156..fbbacf6a1661 100644 --- a/apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/CustomTransition.tsx +++ b/apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/CustomTransition.tsx @@ -5,7 +5,6 @@ import * as React from 'react'; import { Button, StyleSheet, View } from 'react-native'; import Animated, { SharedTransition, - SharedTransitionType, withSpring, } from 'react-native-reanimated'; diff --git a/apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/ProgressTransition.tsx b/apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/ProgressTransition.tsx index f896e3aea1f4..3fc4082010ec 100644 --- a/apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/ProgressTransition.tsx +++ b/apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/ProgressTransition.tsx @@ -15,6 +15,14 @@ import Animated, { FadeInUp, } from 'react-native-reanimated'; +// Image by Freepik +// Image by Freepik +// Image by Freepik +// Image by Freepik +import coffeeImage from './assets/coffee/coffee.png'; +import espressoImage from './assets/coffee/coffee_espresso.png'; +import latteImage from './assets/coffee/coffee_latte.png'; + const AnimatedPressable = Animated.createAnimatedComponent(Pressable); const BACKGROUND = '#fff'; @@ -24,14 +32,6 @@ const ACCENT = '#ba8638'; const TEXT = '#222325'; const TEXT_SECONDARY = '#aeacae'; -// Image by Freepik -const coffeeImage = require('./assets/coffee/coffee.png'); -// Image by Freepik -const espressoImage = require('./assets/coffee/coffee_espresso.png'); -// Image by Freepik -const latteImage = require('./assets/coffee/coffee_latte.png'); -// Image by Freepik - const windowWidth = Dimensions.get('window').width; type StackParamList = { diff --git a/apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/ReducedMotionSharedExample.tsx b/apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/ReducedMotionSharedExample.tsx index 798169b662a4..8c72616266e8 100644 --- a/apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/ReducedMotionSharedExample.tsx +++ b/apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/ReducedMotionSharedExample.tsx @@ -11,72 +11,72 @@ import Animated, { const Stack = createNativeStackNavigator(); -const EXAMPLES = [ - // { - // text: 'default', - // transition: SharedTransition.duration(1000), - // }, - // { - // text: ReduceMotion.Always, - // transition: SharedTransition.duration(1000).reduceMotion( - // ReduceMotion.Always - // ), - // }, - // { - // text: ReduceMotion.Never, - // transition: SharedTransition.duration(1000).reduceMotion( - // ReduceMotion.Never - // ), - // }, - // { - // text: 'custom default', - // transition: SharedTransition.duration(1000).custom((values) => { - // 'worklet'; - // return { - // width: withSpring(values.targetWidth), - // originX: withSpring(values.targetOriginX), - // originY: withSpring(values.targetOriginY), - // }; - // }), - // }, - // { - // text: 'custom always', - // transition: SharedTransition.duration(1000) - // .reduceMotion(ReduceMotion.Always) - // .custom((values) => { - // 'worklet'; - // return { - // width: withSpring(values.targetWidth), - // originX: withSpring(values.targetOriginX), - // originY: withSpring(values.targetOriginY), - // }; - // }), - // }, - // { - // text: 'custom never', - // transition: SharedTransition.duration(1000) - // .reduceMotion(ReduceMotion.Never) - // .custom((values) => { - // 'worklet'; - // return { - // width: withSpring(values.targetWidth, { - // reduceMotion: ReduceMotion.Never, - // }), - // originX: withSpring(values.targetOriginX, { - // reduceMotion: ReduceMotion.Never, - // }), - // originY: withSpring(values.targetOriginY, { - // reduceMotion: ReduceMotion.Never, - // }), - // }; - // }), - // }, -]; +// const EXAMPLES = [ +// { +// text: 'default', +// transition: SharedTransition.duration(1000), +// }, +// { +// text: ReduceMotion.Always, +// transition: SharedTransition.duration(1000).reduceMotion( +// ReduceMotion.Always +// ), +// }, +// { +// text: ReduceMotion.Never, +// transition: SharedTransition.duration(1000).reduceMotion( +// ReduceMotion.Never +// ), +// }, +// { +// text: 'custom default', +// transition: SharedTransition.duration(1000).custom((values) => { +// 'worklet'; +// return { +// width: withSpring(values.targetWidth), +// originX: withSpring(values.targetOriginX), +// originY: withSpring(values.targetOriginY), +// }; +// }), +// }, +// { +// text: 'custom always', +// transition: SharedTransition.duration(1000) +// .reduceMotion(ReduceMotion.Always) +// .custom((values) => { +// 'worklet'; +// return { +// width: withSpring(values.targetWidth), +// originX: withSpring(values.targetOriginX), +// originY: withSpring(values.targetOriginY), +// }; +// }), +// }, +// { +// text: 'custom never', +// transition: SharedTransition.duration(1000) +// .reduceMotion(ReduceMotion.Never) +// .custom((values) => { +// 'worklet'; +// return { +// width: withSpring(values.targetWidth, { +// reduceMotion: ReduceMotion.Never, +// }), +// originX: withSpring(values.targetOriginX, { +// reduceMotion: ReduceMotion.Never, +// }), +// originY: withSpring(values.targetOriginY, { +// reduceMotion: ReduceMotion.Never, +// }), +// }; +// }), +// }, +// ]; function Screen1({ navigation }: NativeStackScreenProps) { return ( - {EXAMPLES.map(({ text, transition }, i) => ( + {/* {EXAMPLES.map(({ text, transition }, i) => ( ) { {text} - ))} + ))} */}