react-native-reanimated 4.4.1 → 4.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Common/cpp/reanimated/CSS/interpolation/transforms/TransformOperationInterpolator.cpp +12 -5
- package/Common/cpp/reanimated/CSS/misc/ViewStylesRepository.cpp +63 -36
- package/Common/cpp/reanimated/CSS/misc/ViewStylesRepository.h +6 -8
- package/Common/cpp/reanimated/CSS/svg/values/SVGStrokeDashArray.cpp +5 -0
- package/Common/cpp/reanimated/Fabric/ReanimatedMountHook.cpp +25 -12
- package/Common/cpp/reanimated/Fabric/ReanimatedMountHook.h +3 -0
- package/Common/cpp/reanimated/Fabric/updates/AnimatedPropsRegistry.cpp +54 -24
- package/Common/cpp/reanimated/Fabric/updates/AnimatedPropsRegistry.h +13 -6
- package/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxyCommon.cpp +6 -2
- package/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxyCommon.h +44 -1
- package/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Experimental.cpp +125 -43
- package/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Legacy.cpp +228 -102
- package/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Legacy.h +4 -0
- package/Common/cpp/reanimated/LayoutAnimations/SharedTransitions.cpp +8 -3
- package/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp +7 -21
- package/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h +0 -16
- package/android/CMakeLists.txt +6 -0
- package/android/build.gradle.kts +21 -1
- package/android/src/main/java/com/swmansion/reanimated/NativeProxy.kt +46 -2
- package/android/src/main/java/com/swmansion/reanimated/keyboard/KeyboardAnimationCallback.kt +8 -1
- package/apple/reanimated/apple/ReanimatedModule.mm +12 -1
- package/compatibility.json +1 -1
- package/lib/module/PropsRegistryGarbageCollector.js +3 -6
- package/lib/module/PropsRegistryGarbageCollector.js.map +1 -1
- package/lib/module/animation/styleAnimation.js +4 -3
- package/lib/module/animation/styleAnimation.js.map +1 -1
- package/lib/module/common/constants/platform.js +4 -4
- package/lib/module/common/constants/platform.js.map +1 -1
- package/lib/module/createAnimatedComponent/AnimatedComponent.js +1 -1
- package/lib/module/createAnimatedComponent/AnimatedComponent.js.map +1 -1
- package/lib/module/createAnimatedComponent/getViewInfo.js +3 -1
- package/lib/module/createAnimatedComponent/getViewInfo.js.map +1 -1
- package/lib/module/css/component/AnimatedComponent.js +0 -2
- package/lib/module/css/component/AnimatedComponent.js.map +1 -1
- package/lib/module/css/native/managers/CSSManager.js +5 -5
- package/lib/module/css/native/managers/CSSManager.js.map +1 -1
- package/lib/module/css/native/managers/CSSTransitionsManager.js +8 -1
- package/lib/module/css/native/managers/CSSTransitionsManager.js.map +1 -1
- package/lib/module/css/svg/native/processors/stroke.js +9 -5
- package/lib/module/css/svg/native/processors/stroke.js.map +1 -1
- package/lib/module/layoutReanimation/animationBuilder/ComplexAnimationBuilder.js +6 -3
- package/lib/module/layoutReanimation/animationBuilder/ComplexAnimationBuilder.js.map +1 -1
- package/lib/module/layoutReanimation/animationsManager.js +39 -11
- package/lib/module/layoutReanimation/animationsManager.js.map +1 -1
- package/lib/module/mappers.js +1 -1
- package/lib/module/mappers.js.map +1 -1
- package/lib/module/platform-specific/findHostInstance.js +8 -5
- package/lib/module/platform-specific/findHostInstance.js.map +1 -1
- package/lib/module/platform-specific/jsVersion.js +1 -1
- package/lib/module/updateProps/updateProps.js +3 -2
- package/lib/module/updateProps/updateProps.js.map +1 -1
- package/lib/module/valueSetter.js +6 -7
- package/lib/module/valueSetter.js.map +1 -1
- package/lib/typescript/PropsRegistryGarbageCollector.d.ts +0 -1
- package/lib/typescript/PropsRegistryGarbageCollector.d.ts.map +1 -1
- package/lib/typescript/animation/styleAnimation.d.ts +1 -1
- package/lib/typescript/animation/styleAnimation.d.ts.map +1 -1
- package/lib/typescript/common/constants/platform.d.ts.map +1 -1
- package/lib/typescript/createAnimatedComponent/AnimatedComponent.d.ts.map +1 -1
- package/lib/typescript/createAnimatedComponent/commonTypes.d.ts +0 -1
- package/lib/typescript/createAnimatedComponent/commonTypes.d.ts.map +1 -1
- package/lib/typescript/createAnimatedComponent/getViewInfo.d.ts.map +1 -1
- package/lib/typescript/css/component/AnimatedComponent.d.ts +0 -1
- package/lib/typescript/css/component/AnimatedComponent.d.ts.map +1 -1
- package/lib/typescript/css/native/managers/CSSTransitionsManager.d.ts +5 -1
- package/lib/typescript/css/native/managers/CSSTransitionsManager.d.ts.map +1 -1
- package/lib/typescript/css/svg/native/processors/stroke.d.ts.map +1 -1
- package/lib/typescript/layoutReanimation/animationBuilder/ComplexAnimationBuilder.d.ts +17 -14
- package/lib/typescript/layoutReanimation/animationBuilder/ComplexAnimationBuilder.d.ts.map +1 -1
- package/lib/typescript/layoutReanimation/animationsManager.d.ts.map +1 -1
- package/lib/typescript/platform-specific/findHostInstance.d.ts.map +1 -1
- package/lib/typescript/platform-specific/jsVersion.d.ts +1 -1
- package/lib/typescript/platform-specific/types.d.ts +1 -0
- package/lib/typescript/platform-specific/types.d.ts.map +1 -1
- package/lib/typescript/updateProps/updateProps.d.ts.map +1 -1
- package/package.json +3 -3
- package/scripts/reanimated_utils.rb +7 -0
- package/src/PropsRegistryGarbageCollector.ts +3 -6
- package/src/animation/styleAnimation.ts +5 -3
- package/src/common/constants/platform.ts +4 -4
- package/src/createAnimatedComponent/AnimatedComponent.tsx +3 -1
- package/src/createAnimatedComponent/commonTypes.ts +0 -1
- package/src/createAnimatedComponent/getViewInfo.ts +3 -1
- package/src/css/component/AnimatedComponent.tsx +0 -2
- package/src/css/native/managers/CSSManager.ts +8 -8
- package/src/css/native/managers/CSSTransitionsManager.ts +9 -2
- package/src/css/svg/native/processors/stroke.ts +9 -6
- package/src/layoutReanimation/animationBuilder/ComplexAnimationBuilder.ts +39 -36
- package/src/layoutReanimation/animationsManager.ts +41 -11
- package/src/mappers.ts +1 -1
- package/src/platform-specific/findHostInstance.ts +7 -7
- package/src/platform-specific/jsVersion.ts +1 -1
- package/src/platform-specific/types.ts +2 -0
- package/src/updateProps/updateProps.ts +3 -2
- package/src/valueSetter.ts +7 -7
package/Common/cpp/reanimated/CSS/interpolation/transforms/TransformOperationInterpolator.cpp
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
#include <reanimated/CSS/interpolation/transforms/operations/skew.h>
|
|
6
6
|
#include <reanimated/CSS/interpolation/transforms/operations/translate.h>
|
|
7
7
|
|
|
8
|
+
#include <cmath>
|
|
9
|
+
#include <limits>
|
|
8
10
|
#include <utility>
|
|
9
11
|
|
|
10
12
|
namespace reanimated::css {
|
|
@@ -31,14 +33,19 @@ std::unique_ptr<StyleOperation> TransformOperationInterpolator<PerspectiveOperat
|
|
|
31
33
|
const std::shared_ptr<StyleOperation> &from,
|
|
32
34
|
const std::shared_ptr<StyleOperation> &to,
|
|
33
35
|
const StyleOperationsInterpolationContext & /* context */) const {
|
|
34
|
-
// TODO - check if this implementation is correct
|
|
35
36
|
const auto &fromValue = std::static_pointer_cast<PerspectiveOperation>(from)->value;
|
|
36
37
|
const auto &toValue = std::static_pointer_cast<PerspectiveOperation>(to)->value;
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
// The default "no perspective" is infinity, so interpolating the distance
|
|
40
|
+
// directly gives inf + t*(d - inf) = NaN. Interpolate in reciprocal space
|
|
41
|
+
// instead (1/inf = 0, matching how perspective enters the matrix as -1/d).
|
|
42
|
+
if (std::isinf(fromValue.value) || std::isinf(toValue.value)) {
|
|
43
|
+
const double fromReciprocal = 1.0 / fromValue.value;
|
|
44
|
+
const double toReciprocal = 1.0 / toValue.value;
|
|
45
|
+
const double reciprocal = fromReciprocal + progress * (toReciprocal - fromReciprocal);
|
|
46
|
+
return std::make_unique<PerspectiveOperation>(
|
|
47
|
+
reciprocal == 0.0 ? std::numeric_limits<double>::infinity() : 1.0 / reciprocal);
|
|
48
|
+
}
|
|
42
49
|
|
|
43
50
|
return std::make_unique<PerspectiveOperation>(fromValue.interpolate(progress, toValue));
|
|
44
51
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#include <reanimated/CSS/misc/ViewStylesRepository.h>
|
|
2
|
+
#include <reanimated/Tools/FeatureFlags.h>
|
|
2
3
|
|
|
3
4
|
#include <memory>
|
|
4
5
|
#include <string>
|
|
@@ -13,13 +14,11 @@ ViewStylesRepository::ViewStylesRepository(
|
|
|
13
14
|
jsi::Value ViewStylesRepository::getNodeProp(
|
|
14
15
|
const std::shared_ptr<const ShadowNode> &shadowNode,
|
|
15
16
|
const std::string &propName) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
auto &cachedNode = shadowNodeCache_[tag];
|
|
19
|
-
updateCacheIfNeeded(cachedNode, shadowNode);
|
|
17
|
+
const auto resolvedNode = getNewestNode(shadowNode);
|
|
18
|
+
const auto *layoutableShadowNode = dynamic_cast<const LayoutableShadowNode *>(resolvedNode.get());
|
|
20
19
|
|
|
21
20
|
if (propName == "width" || propName == "height" || propName == "top" || propName == "left") {
|
|
22
|
-
const auto
|
|
21
|
+
const auto layoutMetrics = layoutableShadowNode ? layoutableShadowNode->layoutMetrics_ : LayoutMetrics{};
|
|
23
22
|
|
|
24
23
|
if (propName == "width") {
|
|
25
24
|
return {layoutMetrics.frame.size.width};
|
|
@@ -31,7 +30,13 @@ jsi::Value ViewStylesRepository::getNodeProp(
|
|
|
31
30
|
return {layoutMetrics.frame.origin.x};
|
|
32
31
|
}
|
|
33
32
|
} else {
|
|
34
|
-
|
|
33
|
+
if (!layoutableShadowNode) {
|
|
34
|
+
return jsi::Value::undefined();
|
|
35
|
+
}
|
|
36
|
+
const auto viewProps = std::static_pointer_cast<const ViewProps>(resolvedNode->getProps());
|
|
37
|
+
if (!viewProps) {
|
|
38
|
+
return jsi::Value::undefined();
|
|
39
|
+
}
|
|
35
40
|
|
|
36
41
|
if (propName == "opacity") {
|
|
37
42
|
return {viewProps->opacity};
|
|
@@ -48,15 +53,7 @@ jsi::Value ViewStylesRepository::getNodeProp(
|
|
|
48
53
|
jsi::Value ViewStylesRepository::getParentNodeProp(
|
|
49
54
|
const std::shared_ptr<const ShadowNode> &shadowNode,
|
|
50
55
|
const std::string &propName) {
|
|
51
|
-
const auto
|
|
52
|
-
const auto &shadowTreeRegistry = uiManager_->getShadowTreeRegistry();
|
|
53
|
-
|
|
54
|
-
std::shared_ptr<const ShadowNode> parentNode = nullptr;
|
|
55
|
-
|
|
56
|
-
shadowTreeRegistry.visit(surfaceId, [&](ShadowTree const &shadowTree) {
|
|
57
|
-
auto currentRevision = shadowTree.getCurrentRevision();
|
|
58
|
-
parentNode = dom::getParentNode(currentRevision.rootShadowNode, *shadowNode);
|
|
59
|
-
});
|
|
56
|
+
const auto parentNode = getParentNode(shadowNode);
|
|
60
57
|
|
|
61
58
|
if (!parentNode) {
|
|
62
59
|
return jsi::Value::undefined();
|
|
@@ -65,37 +62,67 @@ jsi::Value ViewStylesRepository::getParentNodeProp(
|
|
|
65
62
|
return getNodeProp(parentNode, propName);
|
|
66
63
|
}
|
|
67
64
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (
|
|
71
|
-
|
|
65
|
+
std::shared_ptr<const ShadowNode> ViewStylesRepository::getNewestNode(
|
|
66
|
+
const std::shared_ptr<const ShadowNode> &shadowNode) const {
|
|
67
|
+
if constexpr (StaticFeatureFlags::getFlag("USE_ANIMATION_BACKEND")) {
|
|
68
|
+
// Backend mode has no commit hook (it returns before taking the updates-registry
|
|
69
|
+
// lock), so reading the live tree here cannot deadlock and needs no snapshot.
|
|
70
|
+
const auto newestNode = uiManager_->getNewestCloneOfShadowNode(*shadowNode);
|
|
71
|
+
return newestNode ? newestNode : shadowNode;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
// Resolve shadowNode against the last mounted root without the ShadowTree lock (the
|
|
75
|
+
// deadlock) that getNewestCloneOfShadowNode takes, falling back to the passed node.
|
|
76
|
+
// Mirrors RN's getShadowNodeInSubtree:
|
|
77
|
+
// https://github.com/facebook/react-native/blob/v0.86.0-rc.3/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp#L339
|
|
78
|
+
const auto it = lastMountedRootBySurface_.find(shadowNode->getSurfaceId());
|
|
79
|
+
if (it == lastMountedRootBySurface_.end()) {
|
|
80
|
+
return shadowNode;
|
|
81
|
+
}
|
|
82
|
+
const auto &root = it->second;
|
|
76
83
|
|
|
77
|
-
|
|
78
|
-
|
|
84
|
+
if (ShadowNode::sameFamily(*root, *shadowNode)) {
|
|
85
|
+
return root;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const auto ancestors = shadowNode->getFamily().getAncestors(*root);
|
|
89
|
+
if (ancestors.empty()) {
|
|
90
|
+
return shadowNode;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const auto &deepest = ancestors.back();
|
|
94
|
+
return deepest.first.get().getChildren().at(deepest.second);
|
|
79
95
|
}
|
|
80
96
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
97
|
+
std::shared_ptr<const ShadowNode> ViewStylesRepository::getParentNode(
|
|
98
|
+
const std::shared_ptr<const ShadowNode> &shadowNode) const {
|
|
99
|
+
if constexpr (StaticFeatureFlags::getFlag("USE_ANIMATION_BACKEND")) {
|
|
100
|
+
// Backend mode resolves against the live tree (see getNewestNode).
|
|
101
|
+
std::shared_ptr<const ShadowNode> parentNode = nullptr;
|
|
102
|
+
uiManager_->getShadowTreeRegistry().visit(shadowNode->getSurfaceId(), [&](const ShadowTree &shadowTree) {
|
|
103
|
+
parentNode = dom::getParentNode(shadowTree.getCurrentRevision().rootShadowNode, *shadowNode);
|
|
104
|
+
});
|
|
105
|
+
return parentNode;
|
|
106
|
+
}
|
|
85
107
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
return;
|
|
108
|
+
const auto it = lastMountedRootBySurface_.find(shadowNode->getSurfaceId());
|
|
109
|
+
if (it == lastMountedRootBySurface_.end()) {
|
|
110
|
+
return nullptr;
|
|
90
111
|
}
|
|
112
|
+
return dom::getParentNode(it->second, *shadowNode);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
void ViewStylesRepository::setLastMountedRoot(const RootShadowNode::Shared &rootShadowNode) {
|
|
116
|
+
lastMountedRootBySurface_[rootShadowNode->getSurfaceId()] = rootShadowNode;
|
|
117
|
+
}
|
|
91
118
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
119
|
+
folly::dynamic ViewStylesRepository::getStyleProp(const Tag tag, const PropertyPath &propertyPath) {
|
|
120
|
+
auto animatedValue = getPropertyValue(animatedPropsRegistry_->get(tag), propertyPath);
|
|
121
|
+
if (!animatedValue.isNull()) {
|
|
122
|
+
return animatedValue;
|
|
95
123
|
}
|
|
96
124
|
|
|
97
|
-
|
|
98
|
-
cachedNode.viewProps = std::static_pointer_cast<const ViewProps>(newestCloneOfShadowNode->getProps());
|
|
125
|
+
return getPropertyValue(staticPropsRegistry_->get(tag), propertyPath);
|
|
99
126
|
}
|
|
100
127
|
|
|
101
128
|
folly::dynamic ViewStylesRepository::getPropertyValue(const folly::dynamic &value, const PropertyPath &propertyPath) {
|
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
#include <reanimated/CSS/registries/StaticPropsRegistry.h>
|
|
5
5
|
#include <reanimated/Fabric/updates/AnimatedPropsRegistry.h>
|
|
6
6
|
|
|
7
|
+
#include <react/renderer/components/root/RootShadowNode.h>
|
|
7
8
|
#include <react/renderer/components/view/ViewProps.h>
|
|
8
9
|
#include <react/renderer/core/LayoutableShadowNode.h>
|
|
9
10
|
#include <react/renderer/dom/DOM.h>
|
|
11
|
+
#include <react/renderer/uimanager/UIManager.h>
|
|
10
12
|
|
|
11
13
|
#include <memory>
|
|
12
14
|
#include <string>
|
|
@@ -17,11 +19,6 @@ namespace reanimated::css {
|
|
|
17
19
|
using namespace facebook;
|
|
18
20
|
using namespace react;
|
|
19
21
|
|
|
20
|
-
struct CachedShadowNode {
|
|
21
|
-
LayoutMetrics layoutMetrics;
|
|
22
|
-
std::shared_ptr<const ViewProps> viewProps;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
22
|
class ViewStylesRepository {
|
|
26
23
|
public:
|
|
27
24
|
ViewStylesRepository(
|
|
@@ -36,16 +33,17 @@ class ViewStylesRepository {
|
|
|
36
33
|
jsi::Value getParentNodeProp(const std::shared_ptr<const ShadowNode> &shadowNode, const std::string &propName);
|
|
37
34
|
folly::dynamic getStyleProp(Tag tag, const PropertyPath &propertyPath);
|
|
38
35
|
|
|
39
|
-
void
|
|
36
|
+
void setLastMountedRoot(const RootShadowNode::Shared &rootShadowNode);
|
|
40
37
|
|
|
41
38
|
private:
|
|
42
39
|
std::shared_ptr<UIManager> uiManager_;
|
|
43
40
|
std::shared_ptr<StaticPropsRegistry> staticPropsRegistry_;
|
|
44
41
|
std::shared_ptr<AnimatedPropsRegistry> animatedPropsRegistry_;
|
|
45
42
|
|
|
46
|
-
std::unordered_map<
|
|
43
|
+
std::unordered_map<SurfaceId, RootShadowNode::Shared> lastMountedRootBySurface_;
|
|
47
44
|
|
|
48
|
-
|
|
45
|
+
std::shared_ptr<const ShadowNode> getNewestNode(const std::shared_ptr<const ShadowNode> &shadowNode) const;
|
|
46
|
+
std::shared_ptr<const ShadowNode> getParentNode(const std::shared_ptr<const ShadowNode> &shadowNode) const;
|
|
49
47
|
|
|
50
48
|
static folly::dynamic getPropertyValue(const folly::dynamic &value, const PropertyPath &propertyPath);
|
|
51
49
|
};
|
|
@@ -43,6 +43,11 @@ bool SVGStrokeDashArray::canConstruct(const folly::dynamic &value) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
folly::dynamic SVGStrokeDashArray::toDynamic() const {
|
|
46
|
+
// Empty means "no dashing" - emit null instead of [], which react-native-svg
|
|
47
|
+
// would feed to Android's DashPathEffect that requires at least 2 intervals.
|
|
48
|
+
if (values.empty()) {
|
|
49
|
+
return nullptr;
|
|
50
|
+
}
|
|
46
51
|
folly::dynamic array = folly::dynamic::array;
|
|
47
52
|
array.reserve(values.size());
|
|
48
53
|
for (const auto &value : values) {
|
|
@@ -9,8 +9,12 @@ namespace reanimated {
|
|
|
9
9
|
ReanimatedMountHook::ReanimatedMountHook(
|
|
10
10
|
const std::shared_ptr<UIManager> &uiManager,
|
|
11
11
|
const std::shared_ptr<UpdatesRegistryManager> &updatesRegistryManager,
|
|
12
|
+
const std::shared_ptr<css::ViewStylesRepository> &viewStylesRepository,
|
|
12
13
|
const std::function<void()> &requestFlush)
|
|
13
|
-
: uiManager_(uiManager),
|
|
14
|
+
: uiManager_(uiManager),
|
|
15
|
+
updatesRegistryManager_(updatesRegistryManager),
|
|
16
|
+
viewStylesRepository_(viewStylesRepository),
|
|
17
|
+
requestFlush_(requestFlush) {
|
|
14
18
|
uiManager_->registerMountHook(*this);
|
|
15
19
|
}
|
|
16
20
|
|
|
@@ -26,24 +30,33 @@ void ReanimatedMountHook::shadowTreeDidMount(
|
|
|
26
30
|
auto reaShadowNode = std::reinterpret_pointer_cast<ReanimatedCommitShadowNode>(
|
|
27
31
|
std::const_pointer_cast<RootShadowNode>(rootShadowNode));
|
|
28
32
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
// We mark reanimated commits with ReanimatedMountTrait. We don't want other
|
|
34
|
+
// shadow nodes to use this trait, but since this rootShadowNode is Shared,
|
|
35
|
+
// we don't have that guarantee. That's why we also unset this trait in the
|
|
36
|
+
// commit hook. We remove it here mainly for the sake of cleanliness.
|
|
37
|
+
const bool isReanimatedMount = reaShadowNode->hasReanimatedMountTrait();
|
|
38
|
+
if (isReanimatedMount) {
|
|
34
39
|
reaShadowNode->unsetReanimatedMountTrait();
|
|
35
|
-
return;
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
{
|
|
39
43
|
auto lock = updatesRegistryManager_->lock();
|
|
44
|
+
// Record the mounted tree for relative-length resolution.
|
|
45
|
+
viewStylesRepository_->setLastMountedRoot(rootShadowNode);
|
|
46
|
+
|
|
47
|
+
// Always drain removable nodes, even on Reanimated's own commits. While CSS
|
|
48
|
+
// animations run every mount carries the mount trait, so returning early here
|
|
49
|
+
// would skip removals for the whole animation and leak unmounted nodes if the
|
|
50
|
+
// tree is torn down mid-animation.
|
|
40
51
|
updatesRegistryManager_->handleNodeRemovals(*rootShadowNode);
|
|
41
52
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
if (!isReanimatedMount) {
|
|
54
|
+
// When a commit from React Native has finished, we reset the skip commit
|
|
55
|
+
// flag in order to allow Reanimated to commit its tree.
|
|
56
|
+
updatesRegistryManager_->unpauseReanimatedCommits();
|
|
57
|
+
if (updatesRegistryManager_->shouldCommitAfterPause()) {
|
|
58
|
+
requestFlush_();
|
|
59
|
+
}
|
|
47
60
|
}
|
|
48
61
|
}
|
|
49
62
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
|
+
#include <reanimated/CSS/misc/ViewStylesRepository.h>
|
|
3
4
|
#include <reanimated/Fabric/ShadowTreeCloner.h>
|
|
4
5
|
#include <reanimated/Fabric/updates/UpdatesRegistryManager.h>
|
|
5
6
|
|
|
@@ -16,6 +17,7 @@ class ReanimatedMountHook : public UIManagerMountHook {
|
|
|
16
17
|
ReanimatedMountHook(
|
|
17
18
|
const std::shared_ptr<UIManager> &uiManager,
|
|
18
19
|
const std::shared_ptr<UpdatesRegistryManager> &updatesRegistryManager,
|
|
20
|
+
const std::shared_ptr<css::ViewStylesRepository> &viewStylesRepository,
|
|
19
21
|
const std::function<void()> &requestFlush);
|
|
20
22
|
~ReanimatedMountHook() noexcept override;
|
|
21
23
|
|
|
@@ -24,6 +26,7 @@ class ReanimatedMountHook : public UIManagerMountHook {
|
|
|
24
26
|
private:
|
|
25
27
|
const std::shared_ptr<UIManager> uiManager_;
|
|
26
28
|
const std::shared_ptr<UpdatesRegistryManager> updatesRegistryManager_;
|
|
29
|
+
const std::shared_ptr<css::ViewStylesRepository> viewStylesRepository_;
|
|
27
30
|
const std::function<void()> requestFlush_;
|
|
28
31
|
};
|
|
29
32
|
|
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
|
|
6
6
|
#include <react/debug/react_native_assert.h>
|
|
7
7
|
|
|
8
|
+
#include <functional>
|
|
8
9
|
#include <memory>
|
|
9
10
|
#include <utility>
|
|
11
|
+
#include <vector>
|
|
10
12
|
|
|
11
13
|
namespace reanimated {
|
|
12
14
|
|
|
@@ -35,26 +37,65 @@ void AnimatedPropsRegistry::update(jsi::Runtime &rt, const jsi::Value &operation
|
|
|
35
37
|
addUpdatesToBatch(shadowNode->getFamilyShared(), jsi::dynamicFromValue(rt, updates));
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
// When USE_ANIMATION_BACKEND is enabled, updates bypass `updatesRegistry_`,
|
|
41
|
+
// so entries added to `timestampMap_` would never be synced and thus never
|
|
42
|
+
// evicted, leaking until view unmount.
|
|
43
|
+
if constexpr (
|
|
44
|
+
StaticFeatureFlags::getFlag("FORCE_REACT_RENDER_FOR_SETTLED_ANIMATIONS") &&
|
|
45
|
+
!StaticFeatureFlags::getFlag("USE_ANIMATION_BACKEND")) {
|
|
46
|
+
const auto tag = shadowNode->getTag();
|
|
47
|
+
timestampMap_[tag] = timestamp;
|
|
48
|
+
// If JS already has a `settledProps` snapshot for this tag, it is now
|
|
49
|
+
// stale — schedule a refresh on the next `collectSettledUpdates`.
|
|
50
|
+
if (syncedTags_.erase(tag) > 0) {
|
|
51
|
+
invalidatedTags_.insert(tag);
|
|
52
|
+
}
|
|
40
53
|
}
|
|
41
54
|
}
|
|
42
55
|
}
|
|
43
56
|
|
|
44
|
-
jsi::Value AnimatedPropsRegistry::
|
|
45
|
-
jsi::Runtime &rt,
|
|
46
|
-
const double timestamp,
|
|
47
|
-
const double cleanupTimestamp) {
|
|
57
|
+
jsi::Value AnimatedPropsRegistry::collectSettledUpdates(jsi::Runtime &rt, const double settledTimestamp) {
|
|
48
58
|
react_native_assert(UpdatesRegistryManager::isLockedByCurrentThread());
|
|
49
|
-
removeUpdatesOlderThanTimestamp(cleanupTimestamp);
|
|
50
59
|
|
|
51
60
|
std::vector<std::pair<Tag, std::reference_wrapper<const folly::dynamic>>> updates;
|
|
52
61
|
|
|
53
|
-
for (
|
|
54
|
-
auto
|
|
55
|
-
|
|
56
|
-
|
|
62
|
+
for (auto it = updatesRegistry_.begin(); it != updatesRegistry_.end();) {
|
|
63
|
+
const auto viewTag = it->first;
|
|
64
|
+
|
|
65
|
+
if (syncedTags_.contains(viewTag)) {
|
|
66
|
+
// React already has the latest value for this tag (synced on a previous
|
|
67
|
+
// call, so the `settledProps` state is committed by now) — the registry
|
|
68
|
+
// entry is redundant. `syncedTags_` is intentionally retained to detect
|
|
69
|
+
// re-animation staleness. Note that `syncedTags_` and `invalidatedTags_`
|
|
70
|
+
// are disjoint — `update()` moves tags from the former to the latter.
|
|
71
|
+
timestampMap_.erase(viewTag);
|
|
72
|
+
it = updatesRegistry_.erase(it);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const auto timestampIt = timestampMap_.find(viewTag);
|
|
77
|
+
if (timestampIt == timestampMap_.end()) {
|
|
78
|
+
++it;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const bool isSettled = timestampIt->second < settledTimestamp;
|
|
82
|
+
const auto invalidatedIt = invalidatedTags_.find(viewTag);
|
|
83
|
+
const bool isInvalidated = invalidatedIt != invalidatedTags_.end();
|
|
84
|
+
if (isSettled || isInvalidated) {
|
|
85
|
+
updates.emplace_back(viewTag, std::cref(it->second.second));
|
|
86
|
+
if (isSettled) {
|
|
87
|
+
// Only settled-path tags are tracked as "synced" so that an ongoing
|
|
88
|
+
// animation doesn't re-trigger an invalidation/sync on every GC tick.
|
|
89
|
+
syncedTags_.insert(viewTag);
|
|
90
|
+
}
|
|
91
|
+
if (isInvalidated) {
|
|
92
|
+
// Only erase serviced invalidations; if a tag was invalidated but the
|
|
93
|
+
// matching update batch hasn't been flushed into updatesRegistry_ yet,
|
|
94
|
+
// we leave the entry so the next sync picks it up.
|
|
95
|
+
invalidatedTags_.erase(invalidatedIt);
|
|
96
|
+
}
|
|
57
97
|
}
|
|
98
|
+
++it;
|
|
58
99
|
}
|
|
59
100
|
|
|
60
101
|
const jsi::Array array(rt, updates.size());
|
|
@@ -69,22 +110,11 @@ jsi::Value AnimatedPropsRegistry::getUpdatesOlderThanTimestamp(
|
|
|
69
110
|
return jsi::Value(rt, array);
|
|
70
111
|
}
|
|
71
112
|
|
|
72
|
-
void AnimatedPropsRegistry::removeUpdatesOlderThanTimestamp(const double timestamp) {
|
|
73
|
-
for (auto it = timestampMap_.begin(); it != timestampMap_.end();) {
|
|
74
|
-
const auto viewTag = it->first;
|
|
75
|
-
const auto viewTimestamp = it->second;
|
|
76
|
-
if (viewTimestamp < timestamp) {
|
|
77
|
-
it = timestampMap_.erase(it);
|
|
78
|
-
updatesRegistry_.erase(viewTag);
|
|
79
|
-
} else {
|
|
80
|
-
it++;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
113
|
void AnimatedPropsRegistry::removeTag(const Tag tag) {
|
|
86
114
|
updatesRegistry_.erase(tag);
|
|
87
115
|
timestampMap_.erase(tag);
|
|
116
|
+
syncedTags_.erase(tag);
|
|
117
|
+
invalidatedTags_.erase(tag);
|
|
88
118
|
}
|
|
89
119
|
|
|
90
120
|
} // namespace reanimated
|
|
@@ -4,10 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
#include <react/renderer/uimanager/UIManager.h>
|
|
6
6
|
|
|
7
|
-
#include <memory>
|
|
8
|
-
#include <string>
|
|
9
7
|
#include <unordered_map>
|
|
10
|
-
#include <
|
|
8
|
+
#include <unordered_set>
|
|
11
9
|
|
|
12
10
|
namespace reanimated {
|
|
13
11
|
|
|
@@ -15,13 +13,22 @@ class AnimatedPropsRegistry : public UpdatesRegistry {
|
|
|
15
13
|
public:
|
|
16
14
|
void update(jsi::Runtime &rt, const jsi::Value &operations, double timestamp);
|
|
17
15
|
|
|
18
|
-
///
|
|
19
|
-
|
|
16
|
+
/// Returns updates that settled (received no update since `settledTimestamp`)
|
|
17
|
+
/// or whose synced `settledProps` snapshot was invalidated by a fresh update.
|
|
18
|
+
/// Also evicts entries that have already been synced to React — by the time
|
|
19
|
+
/// of the next call, the corresponding `settledProps` state is guaranteed to
|
|
20
|
+
/// be committed, so the registry entries are redundant.
|
|
21
|
+
jsi::Value collectSettledUpdates(jsi::Runtime &rt, double settledTimestamp);
|
|
20
22
|
|
|
21
23
|
private:
|
|
22
24
|
std::unordered_map<Tag, double> timestampMap_;
|
|
25
|
+
// Tags whose latest values have already been pushed to React `settledProps`.
|
|
26
|
+
// Intentionally retained after eviction to detect re-animation staleness.
|
|
27
|
+
std::unordered_set<Tag> syncedTags_;
|
|
28
|
+
// Tags that were synced to React but received a fresh worklet update since;
|
|
29
|
+
// their `settledProps` are stale and need to be refreshed on the next sync.
|
|
30
|
+
std::unordered_set<Tag> invalidatedTags_;
|
|
23
31
|
|
|
24
|
-
void removeUpdatesOlderThanTimestamp(double timestamp);
|
|
25
32
|
void removeTag(Tag tag) override;
|
|
26
33
|
};
|
|
27
34
|
|
|
@@ -37,8 +37,12 @@ const facebook::react::ShadowNode *findInShadowTreeByTag(const facebook::react::
|
|
|
37
37
|
|
|
38
38
|
void LayoutAnimationsProxyCommon::restoreOpacityInCaseOfFlakyEnteringAnimation(SurfaceId surfaceId) const {
|
|
39
39
|
std::vector<std::pair<double, Tag>> opacityToRestore;
|
|
40
|
-
for (const auto tag :
|
|
41
|
-
const auto
|
|
40
|
+
for (const auto tag : maybeSettledAnimationTags_) {
|
|
41
|
+
const auto layoutAnimationIt = layoutAnimations_.find(tag);
|
|
42
|
+
if (layoutAnimationIt == layoutAnimations_.end() || !layoutAnimationIt->second.isSettled()) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const auto &opacity = layoutAnimationIt->second.opacity;
|
|
42
46
|
if (opacity.has_value()) {
|
|
43
47
|
opacityToRestore.emplace_back(std::pair<double, Tag>{opacity.value(), tag});
|
|
44
48
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
3
|
#include <jsi/jsi.h>
|
|
4
|
+
#include <react/debug/react_native_assert.h>
|
|
4
5
|
#include <react/renderer/componentregistry/ComponentDescriptorFactory.h>
|
|
5
6
|
#include <react/renderer/mounting/MountingOverrideDelegate.h>
|
|
6
7
|
#include <react/renderer/uimanager/UIManager.h>
|
|
@@ -11,6 +12,7 @@
|
|
|
11
12
|
#include <memory>
|
|
12
13
|
#include <optional>
|
|
13
14
|
#include <unordered_map>
|
|
15
|
+
#include <unordered_set>
|
|
14
16
|
#include <vector>
|
|
15
17
|
|
|
16
18
|
namespace reanimated {
|
|
@@ -22,8 +24,37 @@ struct LayoutAnimation {
|
|
|
22
24
|
bool isViewAlreadyMounted = false;
|
|
23
25
|
int count = 1;
|
|
24
26
|
LayoutAnimation &operator=(const LayoutAnimation &other) = default;
|
|
27
|
+
|
|
28
|
+
bool isSettled() const {
|
|
29
|
+
return count == 0;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
#ifdef ANDROID
|
|
34
|
+
// Bookkeeping for animation starts that were scheduled onto the UI thread but
|
|
35
|
+
// haven't run yet — see `pendingStarts_` below.
|
|
36
|
+
struct PendingStart {
|
|
37
|
+
int count = 0;
|
|
38
|
+
uint64_t handle = 0;
|
|
25
39
|
};
|
|
26
40
|
|
|
41
|
+
// Removes one pending start for the given tag and returns whether it was
|
|
42
|
+
// cancelled since it was scheduled (i.e. its handle is no longer current).
|
|
43
|
+
// Call under the proxy mutex.
|
|
44
|
+
inline bool
|
|
45
|
+
consumeIsCancelled(std::unordered_map<Tag, PendingStart> &pendingStarts, const Tag tag, const uint64_t handle) {
|
|
46
|
+
const auto it = pendingStarts.find(tag);
|
|
47
|
+
// every scheduled start keeps its entry alive until it is consumed —
|
|
48
|
+
// cancellations only bump the handle, they never erase
|
|
49
|
+
react_native_assert(it != pendingStarts.end() && "PendingStart not found");
|
|
50
|
+
const bool isCancelled = it->second.handle != handle;
|
|
51
|
+
if (--it->second.count == 0) {
|
|
52
|
+
pendingStarts.erase(it);
|
|
53
|
+
}
|
|
54
|
+
return isCancelled;
|
|
55
|
+
}
|
|
56
|
+
#endif
|
|
57
|
+
|
|
27
58
|
class LayoutAnimationsProxyCommon : public facebook::react::MountingOverrideDelegate {
|
|
28
59
|
public:
|
|
29
60
|
LayoutAnimationsProxyCommon(
|
|
@@ -60,7 +91,7 @@ class LayoutAnimationsProxyCommon : public facebook::react::MountingOverrideDele
|
|
|
60
91
|
virtual void startSurface(const SurfaceId surfaceId);
|
|
61
92
|
|
|
62
93
|
protected:
|
|
63
|
-
mutable std::
|
|
94
|
+
mutable std::unordered_set<Tag> maybeSettledAnimationTags_;
|
|
64
95
|
mutable std::unordered_map<Tag, LayoutAnimation> layoutAnimations_;
|
|
65
96
|
std::shared_ptr<LayoutAnimationsManager> layoutAnimationsManager_;
|
|
66
97
|
std::shared_ptr<const ContextContainer> contextContainer_;
|
|
@@ -75,6 +106,18 @@ class LayoutAnimationsProxyCommon : public facebook::react::MountingOverrideDele
|
|
|
75
106
|
|
|
76
107
|
void restoreOpacityInCaseOfFlakyEnteringAnimation(SurfaceId surfaceId) const;
|
|
77
108
|
|
|
109
|
+
// On Android pullTransaction can run on the JS thread, so animation starts
|
|
110
|
+
// are scheduled onto the UI thread. If `maybeCancelAnimation` is called between the
|
|
111
|
+
// start was scheduled and the lambda runs, it
|
|
112
|
+
// finds no `layoutAnimations_` entry to erase (the start lambda hasn't created it
|
|
113
|
+
// yet) and the cancellation is lost — the stale start would later
|
|
114
|
+
// "resurrect" the animation for a view whose Remove+Delete are already on
|
|
115
|
+
// their way to the mounting layer
|
|
116
|
+
// (https://github.com/software-mansion/react-native-reanimated/issues/7493).
|
|
117
|
+
|
|
118
|
+
// To work around this, we keep a separate `pendingStarts_` map that tracks scheduled starts by tag,
|
|
119
|
+
// with a generation counter to detect cancellations.
|
|
120
|
+
mutable std::unordered_map<Tag, PendingStart> pendingStarts_;
|
|
78
121
|
#endif
|
|
79
122
|
};
|
|
80
123
|
|