From 6ae1904b49dbc720e43e1baede227c09f8584909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Wed, 14 Feb 2024 16:08:29 +0100 Subject: [PATCH 01/79] first attempt --- Common/cpp/Fabric/ReanimatedCommitHook.cpp | 99 +++++++++++++++++-- Common/cpp/Fabric/ReanimatedCommitHook.h | 7 +- .../LayoutAnimationsProxy.cpp | 35 +++++++ .../LayoutAnimations/LayoutAnimationsProxy.h | 33 +++++++ .../NativeModules/NativeReanimatedModule.cpp | 64 ++++++++++-- .../NativeModules/NativeReanimatedModule.h | 6 +- FabricExample/ios/Podfile.lock | 2 +- app/src/App.tsx | 3 +- app/src/examples/EmptyExample.tsx | 35 ++++++- .../LayoutAnimations/DefaultAnimations.tsx | 22 +++-- apple/REAModule.mm | 1 + apple/native/NativeProxy.mm | 26 ++--- .../createAnimatedComponent.tsx | 21 ++++ .../layoutReanimation/animationsManager.ts | 2 + 14 files changed, 309 insertions(+), 47 deletions(-) create mode 100644 Common/cpp/LayoutAnimations/LayoutAnimationsProxy.cpp create mode 100644 Common/cpp/LayoutAnimations/LayoutAnimationsProxy.h diff --git a/Common/cpp/Fabric/ReanimatedCommitHook.cpp b/Common/cpp/Fabric/ReanimatedCommitHook.cpp index dc809e00e6b..56eaa8a6fdf 100644 --- a/Common/cpp/Fabric/ReanimatedCommitHook.cpp +++ b/Common/cpp/Fabric/ReanimatedCommitHook.cpp @@ -12,8 +12,9 @@ namespace reanimated { ReanimatedCommitHook::ReanimatedCommitHook( const std::shared_ptr &propsRegistry, - const std::shared_ptr &uiManager) - : propsRegistry_(propsRegistry), uiManager_(uiManager) { + const std::shared_ptr &uiManager, + std::shared_ptr lap) + : propsRegistry_(propsRegistry), uiManager_(uiManager), lap_(lap) { uiManager_->registerCommitHook(*this); } @@ -21,24 +22,110 @@ ReanimatedCommitHook::~ReanimatedCommitHook() noexcept { uiManager_->unregisterCommitHook(*this); } +ShadowNode::Shared findChild(ShadowNode::Shared root, Tag tag){ + if (root->getTag() == tag){ + return root; + } + for (ShadowNode::Shared child: root->getChildren()){ + auto res = findChild(child, tag); + if (res != nullptr){ + return res; + } + } + return nullptr; +} + +ShadowNode::Shared findParent(ShadowNode::Shared root, Tag tag){ + if (root->getTag() == tag){ + return nullptr; + } + for (ShadowNode::Shared child: root->getChildren()){ + if (child->getTag() == tag){ + return root; + } + auto res = findParent(child, tag); + if (res != nullptr){ + return res; + } + } + return nullptr; +} + RootShadowNode::Unshared ReanimatedCommitHook::shadowTreeWillCommit( ShadowTree const &, - RootShadowNode::Shared const &, + RootShadowNode::Shared const &oldRootShadowNode, #if REACT_NATIVE_MINOR_VERSION >= 73 RootShadowNode::Unshared const &newRootShadowNode) noexcept { #else RootShadowNode::Unshared const &newRootShadowNode) const noexcept { #endif + auto mutations = calculateShadowViewMutations(*oldRootShadowNode, *newRootShadowNode); + +// auto createdViews = std::make_shared>(); + +// for (auto &mutation: mutations){ +// if (mutation.type == ShadowViewMutation::Create){ +// createdViews->insert_or_assign(mutation.newChildShadowView.tag, mutation.newChildShadowView); +// } +// } + auto rootNode = newRootShadowNode->ShadowNode::clone(ShadowNodeFragment{}); + { + std::unique_lock(lap_->mutex); + for (auto &mutation: mutations){ + if (mutation.type == ShadowViewMutation::Create){ + lap_->createdViews_->insert_or_assign(mutation.newChildShadowView.tag, mutation.newChildShadowView); + } else if (mutation.type == ShadowViewMutation::Remove){ + lap_->removedViews_->insert_or_assign(mutation.oldChildShadowView.tag, mutation.oldChildShadowView); + if (lap_->layoutAnimationsManager_->hasLayoutAnimation(mutation.oldChildShadowView.tag, LayoutAnimationType::EXITING)){ +// auto parent = findChild(rootNode, mutation.parentShadowView.tag); + auto child = findChild(oldRootShadowNode, mutation.oldChildShadowView.tag); +// auto childParentTag = child->getFamily().parent_.lock()->tag_; +// auto parent = child ->getFamily().getAncestors() + auto oldParent = findParent(oldRootShadowNode, mutation.oldChildShadowView.tag); + auto oldParentTag = oldParent->getTag(); + auto parent = findChild(rootNode, oldParentTag); + if (!parent){ + continue; + } + rootNode = rootNode->cloneTree(parent->getFamily(), [child](const ShadowNode& node){ + auto children = node.getChildren(); + children.push_back(child); +// auto cloneParentTag = clone->getFamily().parent_.lock()->tag_; +// auto child0ParentTag = children[0]->getFamily().parent_.lock()->tag_; + ShadowNode::Unshared newNode = node.clone({ShadowNodeFragment::propsPlaceholder(), std::make_shared(children)}); + return newNode; + }); +// rootNode = oldRootShadowNode->ShadowNode::clone({}); + } + } else if (mutation.type == ShadowViewMutation::Update){ + lap_->modifiedViews_->insert_or_assign(mutation.newChildShadowView.tag, mutation.oldChildShadowView); + lap_->modifiedViewsTarget_->insert_or_assign(mutation.newChildShadowView.tag, mutation.newChildShadowView); + } + } + + for (auto kv: *(lap_->removedViews_)){ + if (lap_->layoutAnimationsManager_->hasLayoutAnimation(kv.first, LayoutAnimationType::EXITING)) + { + Values values; + values.x = kv.second.layoutMetrics.frame.origin.x; + values.y = kv.second.layoutMetrics.frame.origin.y; + values.width = kv.second.layoutMetrics.frame.size.width; + values.height = kv.second.layoutMetrics.frame.size.height; + lap_->startAnimation(kv.first, LayoutAnimationType::EXITING, values); + printf("exiting"); + } + } + lap_->removedViews_->clear(); + } + if (ReanimatedCommitMarker::isReanimatedCommit()) { // ShadowTree commited by Reanimated, no need to apply updates from // PropsRegistry - return newRootShadowNode; + return std::static_pointer_cast(rootNode); } // ShadowTree not commited by Reanimated, apply updates from PropsRegistry - auto rootNode = newRootShadowNode->ShadowNode::clone(ShadowNodeFragment{}); - { auto lock = propsRegistry_->createLock(); diff --git a/Common/cpp/Fabric/ReanimatedCommitHook.h b/Common/cpp/Fabric/ReanimatedCommitHook.h index 22ebd3c612c..13446cbd5b1 100644 --- a/Common/cpp/Fabric/ReanimatedCommitHook.h +++ b/Common/cpp/Fabric/ReanimatedCommitHook.h @@ -6,6 +6,8 @@ #include #include "PropsRegistry.h" +#include "ShadowView.h" +#include "LayoutAnimationsProxy.h" using namespace facebook::react; @@ -15,7 +17,8 @@ class ReanimatedCommitHook : public UIManagerCommitHook { public: ReanimatedCommitHook( const std::shared_ptr &propsRegistry, - const std::shared_ptr &uiManager); + const std::shared_ptr &uiManager, + std::shared_ptr lap); ~ReanimatedCommitHook() noexcept override; @@ -44,6 +47,8 @@ class ReanimatedCommitHook : public UIManagerCommitHook { std::shared_ptr propsRegistry_; std::shared_ptr uiManager_; + + std::shared_ptr lap_; }; } // namespace reanimated diff --git a/Common/cpp/LayoutAnimations/LayoutAnimationsProxy.cpp b/Common/cpp/LayoutAnimations/LayoutAnimationsProxy.cpp new file mode 100644 index 00000000000..a248c9a50bc --- /dev/null +++ b/Common/cpp/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -0,0 +1,35 @@ +#include "LayoutAnimationsProxy.h" +#include "NativeReanimatedModule.h" + +namespace reanimated { + +void LayoutAnimationsProxy::startAnimation(const int tag, const LayoutAnimationType type, Values values){ + printf("start aniamtion %f %f %f %f", values.x, values.y, values.width, values.height); + nativeReanimatedModule_->uiScheduler_->scheduleOnUI([values, this, tag, type](){ + jsi::Runtime &rt = nativeReanimatedModule_->getUIRuntime(); + jsi::Object yogaValues(rt); + yogaValues.setProperty(rt, "originX", values.x); + yogaValues.setProperty(rt, "originY", values.y); + yogaValues.setProperty(rt, "width", values.width); + yogaValues.setProperty(rt, "height", values.height); + nativeReanimatedModule_->layoutAnimationsManager().startLayoutAnimation(rt, tag, type, yogaValues); + }); +} + +void LayoutAnimationsProxy::startLayoutLayoutAnimation(const int tag, Values currentValues, Values targetValues){ + nativeReanimatedModule_->uiScheduler_->scheduleOnUI([currentValues, targetValues, this, tag](){ + jsi::Runtime &rt = nativeReanimatedModule_->getUIRuntime(); + jsi::Object yogaValues(rt); + yogaValues.setProperty(rt, "currentOriginX", currentValues.x); + yogaValues.setProperty(rt, "currentOriginY", currentValues.y); + yogaValues.setProperty(rt, "currentWidth", currentValues.width); + yogaValues.setProperty(rt, "currentHeight", currentValues.height); + yogaValues.setProperty(rt, "targetOriginX", targetValues.x); + yogaValues.setProperty(rt, "targetOriginY", targetValues.y); + yogaValues.setProperty(rt, "targetWidth", targetValues.width); + yogaValues.setProperty(rt, "targetHeight", targetValues.height); + nativeReanimatedModule_->layoutAnimationsManager().startLayoutAnimation(rt, tag, LayoutAnimationType::LAYOUT, yogaValues); + }); +} + +} diff --git a/Common/cpp/LayoutAnimations/LayoutAnimationsProxy.h b/Common/cpp/LayoutAnimations/LayoutAnimationsProxy.h new file mode 100644 index 00000000000..6b33725a3da --- /dev/null +++ b/Common/cpp/LayoutAnimations/LayoutAnimationsProxy.h @@ -0,0 +1,33 @@ +#pragma once + +#include "LayoutAnimationsManager.h" +#include "PropsRegistry.h" +#include "ShadowView.h" + +namespace reanimated { + +class NativeReanimatedModule; + +using namespace facebook; + +struct Values{ + float width, x, y, height; +}; + +struct LayoutAnimationsProxy{ + std::mutex mutex; + std::shared_ptr> createdViews_ = std::make_shared>(); + std::shared_ptr> removedViews_ = std::make_shared>(); + std::shared_ptr> modifiedViews_ = std::make_shared>(); + std::shared_ptr> modifiedViewsTarget_ = std::make_shared>(); + std::shared_ptr layoutAnimationsManager_; + NativeReanimatedModule* nativeReanimatedModule_; + LayoutAnimationsProxy(std::shared_ptr layoutAnimationsManager_, NativeReanimatedModule* n): layoutAnimationsManager_(layoutAnimationsManager_), nativeReanimatedModule_(n){} + void startAnimation(const int tag, + const LayoutAnimationType type, + Values values); + void startLayoutLayoutAnimation(const int tag, + Values currentValues, Values targetValues); +}; + +} diff --git a/Common/cpp/NativeModules/NativeReanimatedModule.cpp b/Common/cpp/NativeModules/NativeReanimatedModule.cpp index b34579deccf..928214e3af6 100644 --- a/Common/cpp/NativeModules/NativeReanimatedModule.cpp +++ b/Common/cpp/NativeModules/NativeReanimatedModule.cpp @@ -19,6 +19,7 @@ #ifdef RCT_NEW_ARCH_ENABLED #include "ReanimatedCommitMarker.h" #include "ShadowTreeCloner.h" +#include #endif #include "AsyncQueue.h" @@ -30,7 +31,7 @@ #include "Shareables.h" #include "UIRuntimeDecorator.h" #include "WorkletEventHandler.h" - +#include "NativeReanimatedModule.h" #ifdef __ANDROID__ #include #endif @@ -71,7 +72,7 @@ NativeReanimatedModule::NativeReanimatedModule( }), animatedSensorModule_(platformDepMethodsHolder), jsLogger_(std::make_shared(jsScheduler_)), - layoutAnimationsManager_(jsLogger_), + layoutAnimationsManager_(std::make_shared(jsLogger_)), #ifdef RCT_NEW_ARCH_ENABLED synchronouslyUpdateUIPropsFunction_( platformDepMethodsHolder.synchronouslyUpdateUIPropsFunction), @@ -299,7 +300,7 @@ jsi::Value NativeReanimatedModule::configureLayoutAnimation( const jsi::Value &type, const jsi::Value &sharedTransitionTag, const jsi::Value &config) { - layoutAnimationsManager_.configureAnimation( + layoutAnimationsManager_->configureAnimation( viewTag.asNumber(), static_cast(type.asNumber()), sharedTransitionTag.asString(rt).utf8(rt), @@ -332,7 +333,7 @@ jsi::Value NativeReanimatedModule::configureLayoutAnimationBatch( "[Reanimated] Layout animation config must be an object."); } } - layoutAnimationsManager_.configureAnimationBatch(batch); + layoutAnimationsManager_->configureAnimationBatch(batch); return jsi::Value::undefined(); } @@ -340,7 +341,7 @@ void NativeReanimatedModule::setShouldAnimateExiting( jsi::Runtime &rt, const jsi::Value &viewTag, const jsi::Value &shouldAnimate) { - layoutAnimationsManager_.setShouldAnimateExiting( + layoutAnimationsManager_->setShouldAnimateExiting( viewTag.asNumber(), shouldAnimate.getBool()); } @@ -703,11 +704,62 @@ jsi::Value NativeReanimatedModule::measure( return result; } + + void NativeReanimatedModule::initializeFabric( const std::shared_ptr &uiManager) { uiManager_ = uiManager; + Scheduler* scheduler = (Scheduler*)uiManager->getDelegate(); + auto lap = std::make_shared(layoutAnimationsManager_, this); + EventListener el = [this, lap](const RawEvent& event){ + if (event.type == "topLayout"){ + if (!event.eventTarget){ + return false; + } + auto tag = event.eventTarget->getTag(); + { + std::unique_lock(lap->mutex); + if(lap->createdViews_ && lap->createdViews_->contains(tag)){ + lap->createdViews_->erase(tag); + if (layoutAnimationsManager_->hasLayoutAnimation(tag, LayoutAnimationType::ENTERING)) + { + printf("entering"); + } + } + if(lap->modifiedViews_ && lap->modifiedViews_->contains(tag)){ + if (layoutAnimationsManager_->hasLayoutAnimation(tag, LayoutAnimationType::LAYOUT)) + { + auto current = lap->modifiedViews_->at(tag), target = lap->modifiedViewsTarget_->at(tag); + Values currentValues, targetValues; + currentValues.x = current.layoutMetrics.frame.origin.x; + currentValues.y = current.layoutMetrics.frame.origin.y; + currentValues.width = current.layoutMetrics.frame.size.width; + currentValues.height = current.layoutMetrics.frame.size.height; + auto layout = event.eventPayload->asJSIValue(getUIRuntime()).asObject(getUIRuntime()).getProperty(getUIRuntime(), "layout").asObject(getUIRuntime()); + targetValues.x=layout.getProperty(getUIRuntime(), "x").asNumber(); + targetValues.y=layout.getProperty(getUIRuntime(), "y").asNumber(); + targetValues.width=layout.getProperty(getUIRuntime(), "width").asNumber(); + targetValues.height=layout.getProperty(getUIRuntime(), "height").asNumber(); +// targetValues.x = target.layoutMetrics.frame.origin.x; +// targetValues.y = target.layoutMetrics.frame.origin.y; +// targetValues.width = target.layoutMetrics.frame.size.width; +// targetValues.height = target.layoutMetrics.frame.size.height; + lap->startLayoutLayoutAnimation(tag, currentValues, targetValues); + } + lap->modifiedViews_->erase(tag); + lap->modifiedViewsTarget_->erase(tag); + } + + } +// layoutAnimationsManager_.startLayoutAnimation(getUIRuntime(), event.eventTarget->getTag(), LayoutAnimationType::ENTERING, nullptr); +// jsi::Object layout = std::dynamic_pointer_cast(event.eventPayload)->asJSIValue(getUIRuntime()).asObject(getUIRuntime()); +// std::printf("hello"); + } + return false; + }; + scheduler->addEventListener(std::make_shared(el)); commitHook_ = - std::make_shared(propsRegistry_, uiManager_); + std::make_shared(propsRegistry_, uiManager_, lap); #if REACT_NATIVE_MINOR_VERSION >= 73 mountHook_ = std::make_shared(propsRegistry_, uiManager_); diff --git a/Common/cpp/NativeModules/NativeReanimatedModule.h b/Common/cpp/NativeModules/NativeReanimatedModule.h index 9f8c32d4e5c..7cb67967a27 100644 --- a/Common/cpp/NativeModules/NativeReanimatedModule.h +++ b/Common/cpp/NativeModules/NativeReanimatedModule.h @@ -151,14 +151,14 @@ class NativeReanimatedModule : public NativeReanimatedModuleSpec { const jsi::Value &listenerId) override; inline LayoutAnimationsManager &layoutAnimationsManager() { - return layoutAnimationsManager_; + return *layoutAnimationsManager_; } inline jsi::Runtime &getUIRuntime() { return uiWorkletRuntime_->getJSIRuntime(); } - private: + public: void requestAnimationFrame(jsi::Runtime &rt, const jsi::Value &callback); #ifdef RCT_NEW_ARCH_ENABLED @@ -181,7 +181,7 @@ class NativeReanimatedModule : public NativeReanimatedModuleSpec { const std::function onRenderCallback_; AnimatedSensorModule animatedSensorModule_; const std::shared_ptr jsLogger_; - LayoutAnimationsManager layoutAnimationsManager_; + std::shared_ptr layoutAnimationsManager_; #ifdef RCT_NEW_ARCH_ENABLED const SynchronouslyUpdateUIPropsFunction synchronouslyUpdateUIPropsFunction_; diff --git a/FabricExample/ios/Podfile.lock b/FabricExample/ios/Podfile.lock index 3500782f5e0..e3c29785e34 100644 --- a/FabricExample/ios/Podfile.lock +++ b/FabricExample/ios/Podfile.lock @@ -1725,4 +1725,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 8be1b60017e74344d005092a2799dafc33f58ffa -COCOAPODS: 1.14.3 +COCOAPODS: 1.14.2 diff --git a/app/src/App.tsx b/app/src/App.tsx index 544c6b37005..dd954cb38d5 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -226,7 +226,8 @@ export default function App() { component={EXAMPLES[name].screen} options={{ animation: shouldReduceMotion ? 'fade' : 'default', - headerTitle: EXAMPLES[name].title, + headerShown: false, + // headerTitle: EXAMPLES[name].title, title: EXAMPLES[name].title, headerLeft: Platform.OS === 'web' ? BackButton : undefined, }} diff --git a/app/src/examples/EmptyExample.tsx b/app/src/examples/EmptyExample.tsx index 19a20bdce59..f6451626af5 100644 --- a/app/src/examples/EmptyExample.tsx +++ b/app/src/examples/EmptyExample.tsx @@ -1,11 +1,30 @@ -import { Text, StyleSheet, View } from 'react-native'; - +import { StyleSheet, View } from 'react-native'; +import Animated, { + FadeOutLeft, + LinearTransition, +} from 'react-native-reanimated'; import React from 'react'; export default function EmptyExample() { + const [show, setShow] = React.useState(true); + const [refresher, setRefresher] = React.useState(false); + console.log('dupa'); return ( - Hello world! + {show && ( + setShow(!show)} + onLayout={() => {}} + style={styles.box} + exiting={FadeOutLeft} + /> + )} + {}} + layout={LinearTransition} + onTouchStart={() => setRefresher(!refresher)} + style={styles.refresher} + /> ); } @@ -16,4 +35,14 @@ const styles = StyleSheet.create({ alignItems: 'center', justifyContent: 'center', }, + box: { + width: 200, + height: 100, + backgroundColor: 'tomato', + }, + refresher: { + width: 100, + height: 100, + backgroundColor: 'blue', + }, }); diff --git a/app/src/examples/LayoutAnimations/DefaultAnimations.tsx b/app/src/examples/LayoutAnimations/DefaultAnimations.tsx index 6fd6a0a2715..3013b47fd99 100644 --- a/app/src/examples/LayoutAnimations/DefaultAnimations.tsx +++ b/app/src/examples/LayoutAnimations/DefaultAnimations.tsx @@ -104,13 +104,17 @@ const AnimatedBlock = ({ {show ? ( setShow(!show)}> - + {}} + style={styles.animatedBlock} + {...animatedStyle}> {name} ) : null} {!show ? ( {}} entering={ 'entering' in animatedStyle ? undefined : FadeIn.delay(350) }> @@ -128,7 +132,7 @@ const AnimatedBlock = ({ export default function DefaultAnimations() { return ( - Fade in + {/* Fade in + /> */} - Fade out + {/* Fade out + /> */} - - + /> */} + {/* Bounce in + /> */} ); } diff --git a/apple/REAModule.mm b/apple/REAModule.mm index ac42cb8d419..39da9c50354 100644 --- a/apple/REAModule.mm +++ b/apple/REAModule.mm @@ -180,6 +180,7 @@ - (void)setBridge:(RCTBridge *)bridge } _nodesManager = [[REANodesManager alloc] initWithModule:self bridge:self.bridge surfacePresenter:_surfacePresenter]; + _animationsManager = [[REAAnimationsManager alloc] initWithUIManager:bridge.uiManager]; } #else diff --git a/apple/native/NativeProxy.mm b/apple/native/NativeProxy.mm index af6536b03b5..53379ff347d 100644 --- a/apple/native/NativeProxy.mm +++ b/apple/native/NativeProxy.mm @@ -197,16 +197,8 @@ static CFTimeInterval calculateTimestampWithSlowAnimations(CFTimeInterval curren NSDictionary *uiProps = convertJSIObjectToNSDictionary(rt, props); [nodesManager synchronouslyUpdateViewOnUIThread:viewTag props:uiProps]; }; +#endif - auto progressLayoutAnimation = [=](jsi::Runtime &rt, int tag, const jsi::Object &newStyle, bool isSharedTransition) { - // noop - }; - - auto endLayoutAnimation = [=](int tag, bool removeView) { - // noop - }; - -#else // Layout Animations start REAAnimationsManager *animationsManager = reaModule.animationsManager; __weak REAAnimationsManager *weakAnimationsManager = animationsManager; @@ -222,15 +214,14 @@ static CFTimeInterval calculateTimestampWithSlowAnimations(CFTimeInterval curren [weakAnimationsManager endLayoutAnimationForTag:@(tag) removeView:removeView]; }; - auto configurePropsFunction = [reaModule]( - jsi::Runtime &rt, const jsi::Value &uiProps, const jsi::Value &nativeProps) { - NSSet *uiPropsSet = convertProps(rt, uiProps); - NSSet *nativePropsSet = convertProps(rt, nativeProps); - [reaModule.nodesManager configureUiProps:uiPropsSet andNativeProps:nativePropsSet]; - }; +// auto configurePropsFunction = [reaModule]( +// jsi::Runtime &rt, const jsi::Value &uiProps, const jsi::Value &nativeProps) { +// NSSet *uiPropsSet = convertProps(rt, uiProps); +// NSSet *nativePropsSet = convertProps(rt, nativeProps); +// [reaModule.nodesManager configureUiProps:uiPropsSet andNativeProps:nativePropsSet]; +// }; // Layout Animations end -#endif auto getAnimationTimestamp = []() { return calculateTimestampWithSlowAnimations(CACurrentMediaTime()) * 1000; }; @@ -309,7 +300,6 @@ static CFTimeInterval calculateTimestampWithSlowAnimations(CFTimeInterval curren nativeReanimatedModule->performOperations(); } }]; -#else // Layout Animation callbacks setup [animationsManager setAnimationStartingBlock:^(NSNumber *_Nonnull tag, LayoutAnimationType type, NSDictionary *_Nonnull values) { @@ -363,6 +353,8 @@ static CFTimeInterval calculateTimestampWithSlowAnimations(CFTimeInterval curren nativeReanimatedModule->layoutAnimationsManager().cancelLayoutAnimation(rt, [tag intValue]); } }]; + +#else [animationsManager setFindPrecedingViewTagForTransitionBlock:^NSNumber *_Nullable(NSNumber *_Nonnull tag) { if (auto nativeReanimatedModule = weakNativeReanimatedModule.lock()) { diff --git a/src/createAnimatedComponent/createAnimatedComponent.tsx b/src/createAnimatedComponent/createAnimatedComponent.tsx index a50c913b752..345920cd9c8 100644 --- a/src/createAnimatedComponent/createAnimatedComponent.tsx +++ b/src/createAnimatedComponent/createAnimatedComponent.tsx @@ -518,6 +518,8 @@ export function createAnimatedComponent( ? (ref as HTMLElement) : findNodeHandle(ref as Component); + this._viewTag = tag as number; + const { layout, entering, exiting, sharedTransitionTag } = this.props; if ( (layout || entering || exiting || sharedTransitionTag) && @@ -553,6 +555,25 @@ export function createAnimatedComponent( this._sharedElementTransition = sharedElementTransition; } } + if (exiting) { + console.log(tag); + const reduceMotionInExiting = + 'getReduceMotion' in exiting && + typeof exiting.getReduceMotion === 'function' + ? getReduceMotionFromConfig(exiting.getReduceMotion()) + : getReduceMotionFromConfig(); + if (!reduceMotionInExiting) { + updateLayoutAnimations( + tag as number, + LayoutAnimationType.EXITING, + maybeBuild( + exiting, + this.props?.style, + AnimatedComponent.displayName + ) + ); + } + } } if (ref !== this._component) { diff --git a/src/reanimated2/layoutReanimation/animationsManager.ts b/src/reanimated2/layoutReanimation/animationsManager.ts index 5eac76000e6..50514b914ee 100644 --- a/src/reanimated2/layoutReanimation/animationsManager.ts +++ b/src/reanimated2/layoutReanimation/animationsManager.ts @@ -20,6 +20,7 @@ function startObservingProgress( const isSharedTransition = animationType === LayoutAnimationType.SHARED_ELEMENT_TRANSITION; sharedValue.addListener(tag + TAG_OFFSET, () => { + console.log(sharedValue.value); _notifyAboutProgress(tag, sharedValue.value, isSharedTransition); }); } @@ -55,6 +56,7 @@ function createLayoutAnimationManager() { global.ProgressTransitionRegister.onTransitionStart(tag, yogaValues); return; } + console.log('start', tag, type, yogaValues, config); const style = config(yogaValues); let currentAnimation = style.animations; From 96157bb59f652523182248f22f842352d8c79723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20B=C5=82oniarz?= Date: Tue, 20 Feb 2024 15:49:41 +0100 Subject: [PATCH 02/79] semi-working approach (issue with flickering) --- Common/cpp/Fabric/ReanimatedCommitHook.cpp | 22 ++++- .../LayoutAnimationsManager.cpp | 29 ++++++ .../LayoutAnimationsManager.h | 9 +- .../LayoutAnimationsProxy.cpp | 49 +++++++++++ .../LayoutAnimations/LayoutAnimationsProxy.h | 11 ++- .../NativeModules/NativeReanimatedModule.cpp | 39 ++++++-- app/src/App.tsx | 70 ++++++++------- app/src/examples/EmptyExample.tsx | 88 ++++++++++++++++--- .../createAnimatedComponent.tsx | 27 +++++- .../layoutReanimation/animationsManager.ts | 81 ++++++++++++++++- 10 files changed, 361 insertions(+), 64 deletions(-) diff --git a/Common/cpp/Fabric/ReanimatedCommitHook.cpp b/Common/cpp/Fabric/ReanimatedCommitHook.cpp index 56eaa8a6fdf..2652375b37d 100644 --- a/Common/cpp/Fabric/ReanimatedCommitHook.cpp +++ b/Common/cpp/Fabric/ReanimatedCommitHook.cpp @@ -73,7 +73,23 @@ RootShadowNode::Unshared ReanimatedCommitHook::shadowTreeWillCommit( std::unique_lock(lap_->mutex); for (auto &mutation: mutations){ if (mutation.type == ShadowViewMutation::Create){ + lap_->tagToNativeID_->insert_or_assign(mutation.newChildShadowView.tag, mutation.newChildShadowView.props->nativeId); + lap_->transferConfigFromNativeTag(mutation.newChildShadowView.tag); + auto child = findChild(rootNode, mutation.newChildShadowView.tag); + lap_->createdNodes_->insert_or_assign(mutation.newChildShadowView.tag, child); lap_->createdViews_->insert_or_assign(mutation.newChildShadowView.tag, mutation.newChildShadowView); +// rootNode = rootNode->cloneTree(child->getFamily(), [](const ShadowNode& node){ +// ShadowNode::Unshared newNode = node.clone({}); +// auto lNode = dynamic_pointer_cast(newNode); +// lNode->setLayoutMetrics(LayoutMetrics{0,0,10,0}); +// return lNode; +// }); +// if (lap_->layoutAnimationsManager_->hasLayoutAnimation(mutation.newChildShadowView.tag, LayoutAnimationType::ENTERING)){ +//// auto child = findChild(rootNode, mutation.newChildShadowView.tag); +// folly::dynamic x = folly::dynamic::object(); +// x.insert("marginTop", 100.0); +// rootNode = cloneShadowTreeWithNewProps(rootNode, child->getFamily(), RawProps(x)); +// } } else if (mutation.type == ShadowViewMutation::Remove){ lap_->removedViews_->insert_or_assign(mutation.oldChildShadowView.tag, mutation.oldChildShadowView); if (lap_->layoutAnimationsManager_->hasLayoutAnimation(mutation.oldChildShadowView.tag, LayoutAnimationType::EXITING)){ @@ -98,8 +114,10 @@ RootShadowNode::Unshared ReanimatedCommitHook::shadowTreeWillCommit( // rootNode = oldRootShadowNode->ShadowNode::clone({}); } } else if (mutation.type == ShadowViewMutation::Update){ - lap_->modifiedViews_->insert_or_assign(mutation.newChildShadowView.tag, mutation.oldChildShadowView); - lap_->modifiedViewsTarget_->insert_or_assign(mutation.newChildShadowView.tag, mutation.newChildShadowView); + lap_->modifiedViews_->insert_or_assign(mutation.newChildShadowView.tag, findChild(oldRootShadowNode, mutation.oldChildShadowView.tag)); +// auto child = findChild(oldRootShadowNode, mutation.oldChildShadowView.tag); +// auto& lChild = static_cast(*child); + lap_->modifiedViewsTarget_->insert_or_assign(mutation.newChildShadowView.tag, findChild(rootNode, mutation.newChildShadowView.tag)); } } diff --git a/Common/cpp/LayoutAnimations/LayoutAnimationsManager.cpp b/Common/cpp/LayoutAnimations/LayoutAnimationsManager.cpp index 5714d60ae6f..e3720adcdc7 100644 --- a/Common/cpp/LayoutAnimations/LayoutAnimationsManager.cpp +++ b/Common/cpp/LayoutAnimations/LayoutAnimationsManager.cpp @@ -1,6 +1,7 @@ #include "LayoutAnimationsManager.h" #include "CollectionUtils.h" #include "Shareables.h" +#include #ifndef NDEBUG #include @@ -121,6 +122,34 @@ void LayoutAnimationsManager::startLayoutAnimation( config->getJSValue(rt)); } +void LayoutAnimationsManager::startLayoutAnimationWithWrapper( + jsi::Runtime &rt, + facebook::react::ShadowNode::Shared node, + const int tag, + const LayoutAnimationType type, + const jsi::Object &values) { + std::shared_ptr config, viewShareable; + { + auto lock = std::unique_lock(animationsMutex_); + config = getConfigsForType(type)[tag]; + } + // TODO: cache the following!! + jsi::Value layoutAnimationRepositoryAsValue = + rt.global() + .getPropertyAsObject(rt, "global") + .getProperty(rt, "LayoutAnimationsManager"); + jsi::Function startAnimationForTag = + layoutAnimationRepositoryAsValue.getObject(rt).getPropertyAsFunction( + rt, "start2"); + startAnimationForTag.call( + rt, + facebook::react::valueFromShadowNode(rt, node), + jsi::Value(tag), + jsi::Value(static_cast(type)), + values, + config->getJSValue(rt)); +} + void LayoutAnimationsManager::cancelLayoutAnimation( jsi::Runtime &rt, const int tag) const { diff --git a/Common/cpp/LayoutAnimations/LayoutAnimationsManager.h b/Common/cpp/LayoutAnimations/LayoutAnimationsManager.h index db3de239988..dfac4f3cff0 100644 --- a/Common/cpp/LayoutAnimations/LayoutAnimationsManager.h +++ b/Common/cpp/LayoutAnimations/LayoutAnimationsManager.h @@ -13,6 +13,7 @@ #include #include #include +#include "ShadowNode.h" namespace reanimated { @@ -43,6 +44,12 @@ class LayoutAnimationsManager { const int tag, const LayoutAnimationType type, const jsi::Object &values); + void startLayoutAnimationWithWrapper( + jsi::Runtime &rt, + facebook::react::ShadowNode::Shared node, + const int tag, + const LayoutAnimationType type, + const jsi::Object &values); void clearLayoutAnimationConfig(const int tag); void cancelLayoutAnimation(jsi::Runtime &rt, const int tag) const; int findPrecedingViewTagForTransition(const int tag); @@ -53,7 +60,7 @@ class LayoutAnimationsManager { void checkDuplicateSharedTag(const int viewTag, const int screenTag); #endif - private: +// private: std::unordered_map> &getConfigsForType( const LayoutAnimationType type); diff --git a/Common/cpp/LayoutAnimations/LayoutAnimationsProxy.cpp b/Common/cpp/LayoutAnimations/LayoutAnimationsProxy.cpp index a248c9a50bc..9ae9e746ea9 100644 --- a/Common/cpp/LayoutAnimations/LayoutAnimationsProxy.cpp +++ b/Common/cpp/LayoutAnimations/LayoutAnimationsProxy.cpp @@ -16,6 +16,19 @@ void LayoutAnimationsProxy::startAnimation(const int tag, const LayoutAnimationT }); } +void LayoutAnimationsProxy::startAnimationWithWrapper(ShadowNode::Shared node, const int tag, const LayoutAnimationType type, Values values){ + printf("start aniamtion %f %f %f %f", values.x, values.y, values.width, values.height); + nativeReanimatedModule_->uiScheduler_->scheduleOnUI([values, this, tag, type, node](){ + jsi::Runtime &rt = nativeReanimatedModule_->getUIRuntime(); + jsi::Object yogaValues(rt); + yogaValues.setProperty(rt, "originX", values.x); + yogaValues.setProperty(rt, "originY", values.y); + yogaValues.setProperty(rt, "width", values.width); + yogaValues.setProperty(rt, "height", values.height); + nativeReanimatedModule_->layoutAnimationsManager().startLayoutAnimationWithWrapper(rt, node, tag, type, yogaValues); + }); +} + void LayoutAnimationsProxy::startLayoutLayoutAnimation(const int tag, Values currentValues, Values targetValues){ nativeReanimatedModule_->uiScheduler_->scheduleOnUI([currentValues, targetValues, this, tag](){ jsi::Runtime &rt = nativeReanimatedModule_->getUIRuntime(); @@ -32,4 +45,40 @@ void LayoutAnimationsProxy::startLayoutLayoutAnimation(const int tag, Values cur }); } +void LayoutAnimationsProxy::startLayoutLayoutAnimationWithWrapper(ShadowNode::Shared node, const int tag, const LayoutAnimationType type, Values currentValues, Values targetValues){ + nativeReanimatedModule_->uiScheduler_->scheduleOnUI([currentValues, targetValues, this, tag, type, node](){ + jsi::Runtime &rt = nativeReanimatedModule_->getUIRuntime(); + jsi::Object yogaValues(rt); + yogaValues.setProperty(rt, "currentOriginX", currentValues.x); + yogaValues.setProperty(rt, "currentOriginY", currentValues.y); + yogaValues.setProperty(rt, "currentWidth", currentValues.width); + yogaValues.setProperty(rt, "currentHeight", currentValues.height); + yogaValues.setProperty(rt, "targetOriginX", targetValues.x); + yogaValues.setProperty(rt, "targetOriginY", targetValues.y); + yogaValues.setProperty(rt, "targetWidth", targetValues.width); + yogaValues.setProperty(rt, "targetHeight", targetValues.height); + nativeReanimatedModule_->layoutAnimationsManager().startLayoutAnimationWithWrapper(rt, node, tag, type, yogaValues); + }); +} + +void LayoutAnimationsProxy::transferConfigFromNativeTag(const int tag){ + if (!tagToNativeID_->contains(tag)){ + return; + } + auto nativeIDString = tagToNativeID_->at(tag); + if (nativeIDString.empty()){ + return; + } + auto nativeID = stoi(nativeIDString); + std::shared_ptr config = nullptr; + { + auto lock = std::unique_lock(nativeReanimatedModule_->layoutAnimationsManager_->animationsMutex_); + config = layoutAnimationsManager_->enteringAnimations_[nativeID]; + } + auto s = ""; + if (config){ + nativeReanimatedModule_->layoutAnimationsManager_->configureAnimation(tag, LayoutAnimationType::ENTERING, s, config); + } +} + } diff --git a/Common/cpp/LayoutAnimations/LayoutAnimationsProxy.h b/Common/cpp/LayoutAnimations/LayoutAnimationsProxy.h index 6b33725a3da..430763c828f 100644 --- a/Common/cpp/LayoutAnimations/LayoutAnimationsProxy.h +++ b/Common/cpp/LayoutAnimations/LayoutAnimationsProxy.h @@ -16,18 +16,25 @@ struct Values{ struct LayoutAnimationsProxy{ std::mutex mutex; + std::shared_ptr> createdNodes_ = std::make_shared>(); std::shared_ptr> createdViews_ = std::make_shared>(); std::shared_ptr> removedViews_ = std::make_shared>(); - std::shared_ptr> modifiedViews_ = std::make_shared>(); - std::shared_ptr> modifiedViewsTarget_ = std::make_shared>(); + std::shared_ptr> modifiedViews_ = std::make_shared>(); + std::shared_ptr> modifiedViewsTarget_ = std::make_shared>(); + std::shared_ptr> tagToNativeID_ = std::make_shared>(); std::shared_ptr layoutAnimationsManager_; NativeReanimatedModule* nativeReanimatedModule_; LayoutAnimationsProxy(std::shared_ptr layoutAnimationsManager_, NativeReanimatedModule* n): layoutAnimationsManager_(layoutAnimationsManager_), nativeReanimatedModule_(n){} void startAnimation(const int tag, const LayoutAnimationType type, Values values); + void startAnimationWithWrapper(ShadowNode::Shared sn, const int tag, + const LayoutAnimationType type, + Values values); void startLayoutLayoutAnimation(const int tag, Values currentValues, Values targetValues); + void startLayoutLayoutAnimationWithWrapper(ShadowNode::Shared node, const int tag, const LayoutAnimationType type, Values currentValues, Values targetValues); + void transferConfigFromNativeTag(const int tag); }; } diff --git a/Common/cpp/NativeModules/NativeReanimatedModule.cpp b/Common/cpp/NativeModules/NativeReanimatedModule.cpp index 928214e3af6..e3de16afab4 100644 --- a/Common/cpp/NativeModules/NativeReanimatedModule.cpp +++ b/Common/cpp/NativeModules/NativeReanimatedModule.cpp @@ -720,31 +720,52 @@ void NativeReanimatedModule::initializeFabric( { std::unique_lock(lap->mutex); if(lap->createdViews_ && lap->createdViews_->contains(tag)){ + auto view = lap->createdViews_->at(tag); + auto node = lap->createdNodes_->at(tag); lap->createdViews_->erase(tag); + lap->createdNodes_->erase(tag); +// if (!lap->tagToNativeID_->contains(tag)){ +// return false; +// } +// auto nativeIDString = lap->tagToNativeID_->at(tag); +// if (nativeIDString.empty()){ +// return false; +// } +// auto nativeID = stoi(nativeIDString); + printf("entering %d\n", tag); if (layoutAnimationsManager_->hasLayoutAnimation(tag, LayoutAnimationType::ENTERING)) { printf("entering"); + Values currentValues, targetValues; + auto layout = event.eventPayload->asJSIValue(getUIRuntime()).asObject(getUIRuntime()).getProperty(getUIRuntime(), "layout").asObject(getUIRuntime()); + targetValues.x=layout.getProperty(getUIRuntime(), "x").asNumber(); + targetValues.y=layout.getProperty(getUIRuntime(), "y").asNumber(); + targetValues.width=layout.getProperty(getUIRuntime(), "width").asNumber(); + targetValues.height=layout.getProperty(getUIRuntime(), "height").asNumber(); +// lap->startAnimationWithWrapper(node, tag, LayoutAnimationType::ENTERING, targetValues); + lap->startAnimation(tag, LayoutAnimationType::ENTERING, targetValues); } } if(lap->modifiedViews_ && lap->modifiedViews_->contains(tag)){ if (layoutAnimationsManager_->hasLayoutAnimation(tag, LayoutAnimationType::LAYOUT)) { - auto current = lap->modifiedViews_->at(tag), target = lap->modifiedViewsTarget_->at(tag); + auto current = lap->modifiedViews_->at(tag); + auto& currentNode = static_cast(*current); + auto target = lap->modifiedViewsTarget_->at(tag); + auto& targetNode = static_cast(*target); Values currentValues, targetValues; - currentValues.x = current.layoutMetrics.frame.origin.x; - currentValues.y = current.layoutMetrics.frame.origin.y; - currentValues.width = current.layoutMetrics.frame.size.width; - currentValues.height = current.layoutMetrics.frame.size.height; + auto lm = currentNode.getLayoutMetrics(); + currentValues.x = lm.frame.origin.x; + currentValues.y = lm.frame.origin.y; + currentValues.width = lm.frame.size.width; + currentValues.height = lm.frame.size.height; auto layout = event.eventPayload->asJSIValue(getUIRuntime()).asObject(getUIRuntime()).getProperty(getUIRuntime(), "layout").asObject(getUIRuntime()); targetValues.x=layout.getProperty(getUIRuntime(), "x").asNumber(); targetValues.y=layout.getProperty(getUIRuntime(), "y").asNumber(); targetValues.width=layout.getProperty(getUIRuntime(), "width").asNumber(); targetValues.height=layout.getProperty(getUIRuntime(), "height").asNumber(); -// targetValues.x = target.layoutMetrics.frame.origin.x; -// targetValues.y = target.layoutMetrics.frame.origin.y; -// targetValues.width = target.layoutMetrics.frame.size.width; -// targetValues.height = target.layoutMetrics.frame.size.height; lap->startLayoutLayoutAnimation(tag, currentValues, targetValues); +// lap->startLayoutLayoutAnimationWithWrapper(target, tag, LayoutAnimationType::LAYOUT, currentValues, targetValues); } lap->modifiedViews_->erase(tag); lap->modifiedViewsTarget_->erase(tag); diff --git a/app/src/App.tsx b/app/src/App.tsx index dd954cb38d5..e517b8e444a 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -38,6 +38,7 @@ import AsyncStorage from '@react-native-async-storage/async-storage'; import { EXAMPLES } from './examples'; import React from 'react'; import { useReducedMotion } from 'react-native-reanimated'; +import EmptyExample from './examples/EmptyExample'; type RootStackParamList = { [P in keyof typeof EXAMPLES]: undefined } & { Home: undefined; @@ -202,40 +203,41 @@ export default function App() { } return ( - - - AsyncStorage.setItem(PERSISTENCE_KEY, JSON.stringify(state)) - }> - - null : undefined, - }} - /> - {EXAMPLES_NAMES.map((name) => ( - - ))} - - - + + // + // + // AsyncStorage.setItem(PERSISTENCE_KEY, JSON.stringify(state)) + // }> + // + // null : undefined, + // }} + // /> + // {EXAMPLES_NAMES.map((name) => ( + // + // ))} + // + // + // ); } diff --git a/app/src/examples/EmptyExample.tsx b/app/src/examples/EmptyExample.tsx index f6451626af5..ead343499a0 100644 --- a/app/src/examples/EmptyExample.tsx +++ b/app/src/examples/EmptyExample.tsx @@ -1,30 +1,89 @@ -import { StyleSheet, View } from 'react-native'; +import { Button, StyleSheet, View } from 'react-native'; import Animated, { + BounceIn, + BounceInUp, + CurvedTransition, + EntryExitAnimationFunction, + FadeInUp, FadeOutLeft, + FadingTransition, + LayoutAnimationFunction, LinearTransition, + useAnimatedStyle, + useSharedValue, + withTiming, } from 'react-native-reanimated'; -import React from 'react'; +import React, { useEffect } from 'react'; + +const custom: LayoutAnimationFunction = (values) => { + 'worklet'; + return { + animations: { + transform: [{ rotate: withTiming('45deg') }], + opacity: withTiming(1), + }, + initialValues: { transform: [{ rotate: '0deg' }], opacity: 0 }, + }; +}; + +const custom2: EntryExitAnimationFunction = (values) => { + 'worklet'; + console.log(values); + return { + animations: { + originY: withTiming(300), + opacity: 1, + }, + initialValues: { originY: 0, opacity: 1 }, + }; +}; export default function EmptyExample() { const [show, setShow] = React.useState(true); + const [show2, setShow2] = React.useState(false); const [refresher, setRefresher] = React.useState(false); - console.log('dupa'); + const sv = useSharedValue(0); + const animatedStyle = useAnimatedStyle(() => { + return { + // transform: [{ translateX: sv.value }], + height: 100 + sv.value, + }; + }); + + // useEffect(() => { + // sv.value = withTiming(100, { duration: 1000 }); + // }, [sv]); + return ( + {/* */} + {/* */} +