react-native-reanimated 4.3.1 → 4.3.2
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 +59 -37
- package/Common/cpp/reanimated/CSS/misc/ViewStylesRepository.h +11 -8
- package/Common/cpp/reanimated/CSS/registries/CSSAnimationsRegistry.cpp +4 -0
- package/Common/cpp/reanimated/CSS/registries/StaticPropsRegistry.cpp +17 -4
- package/Common/cpp/reanimated/CSS/registries/StaticPropsRegistry.h +5 -0
- 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/LayoutAnimations/LayoutAnimationsProxyCommon.h +38 -0
- package/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Experimental.cpp +106 -33
- package/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Legacy.cpp +165 -92
- package/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp +2 -6
- package/android/CMakeLists.txt +4 -1
- package/android/build.gradle +2 -0
- package/android/generate-stub-pch.gradle.kts +84 -0
- package/android/src/main/cpp/ReanimatedPCH.h +46 -0
- package/android/src/main/java/com/swmansion/reanimated/NativeProxy.java +14 -1
- package/android/src/main/java/com/swmansion/reanimated/keyboard/KeyboardAnimationCallback.java +9 -1
- package/apple/reanimated/apple/ReanimatedModule.mm +12 -1
- package/lib/module/common/constants/platform.js +5 -5
- package/lib/module/common/constants/platform.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/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 +1 -1
- package/lib/module/valueSetter.js.map +1 -1
- package/lib/typescript/common/constants/platform.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/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/lib/typescript/valueSetter.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/common/constants/platform.ts +6 -5
- 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/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 +2 -1
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
|
}
|
|
@@ -13,13 +13,11 @@ ViewStylesRepository::ViewStylesRepository(
|
|
|
13
13
|
jsi::Value ViewStylesRepository::getNodeProp(
|
|
14
14
|
const std::shared_ptr<const ShadowNode> &shadowNode,
|
|
15
15
|
const std::string &propName) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
auto &cachedNode = shadowNodeCache_[tag];
|
|
19
|
-
updateCacheIfNeeded(cachedNode, shadowNode);
|
|
16
|
+
const auto resolvedNode = getNewestNode(shadowNode);
|
|
17
|
+
const auto *layoutableShadowNode = dynamic_cast<const LayoutableShadowNode *>(resolvedNode.get());
|
|
20
18
|
|
|
21
19
|
if (propName == "width" || propName == "height" || propName == "top" || propName == "left") {
|
|
22
|
-
const auto
|
|
20
|
+
const auto layoutMetrics = layoutableShadowNode ? layoutableShadowNode->layoutMetrics_ : LayoutMetrics{};
|
|
23
21
|
|
|
24
22
|
if (propName == "width") {
|
|
25
23
|
return {layoutMetrics.frame.size.width};
|
|
@@ -31,7 +29,13 @@ jsi::Value ViewStylesRepository::getNodeProp(
|
|
|
31
29
|
return {layoutMetrics.frame.origin.x};
|
|
32
30
|
}
|
|
33
31
|
} else {
|
|
34
|
-
|
|
32
|
+
if (!layoutableShadowNode) {
|
|
33
|
+
return jsi::Value::undefined();
|
|
34
|
+
}
|
|
35
|
+
const auto viewProps = std::static_pointer_cast<const ViewProps>(resolvedNode->getProps());
|
|
36
|
+
if (!viewProps) {
|
|
37
|
+
return jsi::Value::undefined();
|
|
38
|
+
}
|
|
35
39
|
|
|
36
40
|
if (propName == "opacity") {
|
|
37
41
|
return {viewProps->opacity};
|
|
@@ -48,15 +52,7 @@ jsi::Value ViewStylesRepository::getNodeProp(
|
|
|
48
52
|
jsi::Value ViewStylesRepository::getParentNodeProp(
|
|
49
53
|
const std::shared_ptr<const ShadowNode> &shadowNode,
|
|
50
54
|
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
|
-
});
|
|
55
|
+
const auto parentNode = getParentNode(shadowNode);
|
|
60
56
|
|
|
61
57
|
if (!parentNode) {
|
|
62
58
|
return jsi::Value::undefined();
|
|
@@ -65,37 +61,63 @@ jsi::Value ViewStylesRepository::getParentNodeProp(
|
|
|
65
61
|
return getNodeProp(parentNode, propName);
|
|
66
62
|
}
|
|
67
63
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
64
|
+
std::shared_ptr<const ShadowNode> ViewStylesRepository::getNewestNode(
|
|
65
|
+
const std::shared_ptr<const ShadowNode> &shadowNode) const {
|
|
66
|
+
// Resolve shadowNode against the last mounted root instead of reading the live
|
|
67
|
+
// ShadowTree: uiManager_->getNewestCloneOfShadowNode takes the ShadowTree commit
|
|
68
|
+
// lock, and this method runs under the updates-registry lock while a concurrent
|
|
69
|
+
// Fabric commit takes the same two locks in the opposite order (the ANR
|
|
70
|
+
// deadlock). Falls back to the passed node. Mirrors RN's getShadowNodeInSubtree:
|
|
71
|
+
// https://github.com/facebook/react-native/blob/v0.86.0-rc.3/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp#L339
|
|
72
|
+
RootShadowNode::Shared root;
|
|
73
|
+
{
|
|
74
|
+
const std::lock_guard<std::mutex> lock{lastMountedRootMutex_};
|
|
75
|
+
const auto it = lastMountedRootBySurface_.find(shadowNode->getSurfaceId());
|
|
76
|
+
if (it == lastMountedRootBySurface_.end()) {
|
|
77
|
+
return shadowNode;
|
|
78
|
+
}
|
|
79
|
+
root = it->second;
|
|
72
80
|
}
|
|
73
81
|
|
|
74
|
-
|
|
75
|
-
|
|
82
|
+
if (ShadowNode::sameFamily(*root, *shadowNode)) {
|
|
83
|
+
return root;
|
|
84
|
+
}
|
|
76
85
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
86
|
+
const auto ancestors = shadowNode->getFamily().getAncestors(*root);
|
|
87
|
+
if (ancestors.empty()) {
|
|
88
|
+
return shadowNode;
|
|
89
|
+
}
|
|
80
90
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
auto newestCloneOfShadowNode = uiManager_->getNewestCloneOfShadowNode(*shadowNode);
|
|
91
|
+
const auto &deepest = ancestors.back();
|
|
92
|
+
return deepest.first.get().getChildren().at(deepest.second);
|
|
93
|
+
}
|
|
85
94
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
95
|
+
std::shared_ptr<const ShadowNode> ViewStylesRepository::getParentNode(
|
|
96
|
+
const std::shared_ptr<const ShadowNode> &shadowNode) const {
|
|
97
|
+
RootShadowNode::Shared root;
|
|
98
|
+
{
|
|
99
|
+
const std::lock_guard<std::mutex> lock{lastMountedRootMutex_};
|
|
100
|
+
const auto it = lastMountedRootBySurface_.find(shadowNode->getSurfaceId());
|
|
101
|
+
if (it == lastMountedRootBySurface_.end()) {
|
|
102
|
+
return nullptr;
|
|
103
|
+
}
|
|
104
|
+
root = it->second;
|
|
90
105
|
}
|
|
106
|
+
return dom::getParentNode(root, *shadowNode);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
void ViewStylesRepository::setLastMountedRoot(const RootShadowNode::Shared &rootShadowNode) {
|
|
110
|
+
const std::lock_guard<std::mutex> lock{lastMountedRootMutex_};
|
|
111
|
+
lastMountedRootBySurface_[rootShadowNode->getSurfaceId()] = rootShadowNode;
|
|
112
|
+
}
|
|
91
113
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
114
|
+
folly::dynamic ViewStylesRepository::getStyleProp(const Tag tag, const PropertyPath &propertyPath) {
|
|
115
|
+
auto animatedValue = getPropertyValue(animatedPropsRegistry_->get(tag), propertyPath);
|
|
116
|
+
if (!animatedValue.isNull()) {
|
|
117
|
+
return animatedValue;
|
|
95
118
|
}
|
|
96
119
|
|
|
97
|
-
|
|
98
|
-
cachedNode.viewProps = std::static_pointer_cast<const ViewProps>(newestCloneOfShadowNode->getProps());
|
|
120
|
+
return getPropertyValue(staticPropsRegistry_->get(tag), propertyPath);
|
|
99
121
|
}
|
|
100
122
|
|
|
101
123
|
folly::dynamic ViewStylesRepository::getPropertyValue(const folly::dynamic &value, const PropertyPath &propertyPath) {
|
|
@@ -4,11 +4,14 @@
|
|
|
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>
|
|
14
|
+
#include <mutex>
|
|
12
15
|
#include <string>
|
|
13
16
|
#include <unordered_map>
|
|
14
17
|
|
|
@@ -17,11 +20,6 @@ namespace reanimated::css {
|
|
|
17
20
|
using namespace facebook;
|
|
18
21
|
using namespace react;
|
|
19
22
|
|
|
20
|
-
struct CachedShadowNode {
|
|
21
|
-
LayoutMetrics layoutMetrics;
|
|
22
|
-
std::shared_ptr<const ViewProps> viewProps;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
23
|
class ViewStylesRepository {
|
|
26
24
|
public:
|
|
27
25
|
ViewStylesRepository(
|
|
@@ -36,16 +34,21 @@ class ViewStylesRepository {
|
|
|
36
34
|
jsi::Value getParentNodeProp(const std::shared_ptr<const ShadowNode> &shadowNode, const std::string &propName);
|
|
37
35
|
folly::dynamic getStyleProp(Tag tag, const PropertyPath &propertyPath);
|
|
38
36
|
|
|
39
|
-
void
|
|
37
|
+
void setLastMountedRoot(const RootShadowNode::Shared &rootShadowNode);
|
|
40
38
|
|
|
41
39
|
private:
|
|
42
40
|
std::shared_ptr<UIManager> uiManager_;
|
|
43
41
|
std::shared_ptr<StaticPropsRegistry> staticPropsRegistry_;
|
|
44
42
|
std::shared_ptr<AnimatedPropsRegistry> animatedPropsRegistry_;
|
|
45
43
|
|
|
46
|
-
|
|
44
|
+
// Transition setup resolves relative lengths on the JS thread without the
|
|
45
|
+
// updates-registry lock, while the mount hook records roots under it, so the
|
|
46
|
+
// map needs its own guard.
|
|
47
|
+
mutable std::mutex lastMountedRootMutex_;
|
|
48
|
+
std::unordered_map<SurfaceId, RootShadowNode::Shared> lastMountedRootBySurface_;
|
|
47
49
|
|
|
48
|
-
|
|
50
|
+
std::shared_ptr<const ShadowNode> getNewestNode(const std::shared_ptr<const ShadowNode> &shadowNode) const;
|
|
51
|
+
std::shared_ptr<const ShadowNode> getParentNode(const std::shared_ptr<const ShadowNode> &shadowNode) const;
|
|
49
52
|
|
|
50
53
|
static folly::dynamic getPropertyValue(const folly::dynamic &value, const PropertyPath &propertyPath);
|
|
51
54
|
};
|
|
@@ -293,6 +293,10 @@ void CSSAnimationsRegistry::applyViewAnimationsStyle(const Tag viewTag, const do
|
|
|
293
293
|
}
|
|
294
294
|
|
|
295
295
|
setInUpdatesRegistry(shadowNode, updatedStyle);
|
|
296
|
+
|
|
297
|
+
if (shadowNode && !updatedStyle.empty()) {
|
|
298
|
+
addUpdatesToBatch(shadowNode, updatedStyle);
|
|
299
|
+
}
|
|
296
300
|
}
|
|
297
301
|
|
|
298
302
|
void CSSAnimationsRegistry::activateDelayedAnimations(const double timestamp) {
|
|
@@ -7,16 +7,26 @@ namespace reanimated::css {
|
|
|
7
7
|
void StaticPropsRegistry::set(jsi::Runtime &rt, const Tag viewTag, const jsi::Value &props) {
|
|
8
8
|
if (props.isNull() || props.isUndefined()) {
|
|
9
9
|
remove(viewTag);
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const auto newProps = dynamicFromValue(rt, props);
|
|
14
|
+
folly::dynamic oldProps;
|
|
15
|
+
{
|
|
16
|
+
std::lock_guard<std::mutex> lock{mutex_};
|
|
17
|
+
const auto it = registry_.find(viewTag);
|
|
18
|
+
if (it != registry_.end()) {
|
|
19
|
+
oldProps = it->second;
|
|
14
20
|
}
|
|
15
21
|
registry_[viewTag] = newProps;
|
|
16
22
|
}
|
|
23
|
+
if (!oldProps.isNull()) {
|
|
24
|
+
notifyObservers(viewTag, oldProps, newProps);
|
|
25
|
+
}
|
|
17
26
|
}
|
|
18
27
|
|
|
19
28
|
folly::dynamic StaticPropsRegistry::get(const Tag viewTag) const {
|
|
29
|
+
std::lock_guard<std::mutex> lock{mutex_};
|
|
20
30
|
auto it = registry_.find(viewTag);
|
|
21
31
|
if (it == registry_.end()) {
|
|
22
32
|
return nullptr;
|
|
@@ -25,14 +35,17 @@ folly::dynamic StaticPropsRegistry::get(const Tag viewTag) const {
|
|
|
25
35
|
}
|
|
26
36
|
|
|
27
37
|
bool StaticPropsRegistry::has(const Tag viewTag) const {
|
|
38
|
+
std::lock_guard<std::mutex> lock{mutex_};
|
|
28
39
|
return registry_.find(viewTag) != registry_.end();
|
|
29
40
|
}
|
|
30
41
|
|
|
31
42
|
void StaticPropsRegistry::remove(const Tag viewTag) {
|
|
43
|
+
std::lock_guard<std::mutex> lock{mutex_};
|
|
32
44
|
registry_.erase(viewTag);
|
|
33
45
|
}
|
|
34
46
|
|
|
35
47
|
bool StaticPropsRegistry::isEmpty() const {
|
|
48
|
+
std::lock_guard<std::mutex> lock{mutex_};
|
|
36
49
|
return registry_.empty() && observers_.empty();
|
|
37
50
|
}
|
|
38
51
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
#include <react/renderer/core/ShadowNode.h>
|
|
4
4
|
|
|
5
|
+
#include <mutex>
|
|
5
6
|
#include <unordered_map>
|
|
6
7
|
|
|
7
8
|
namespace reanimated::css {
|
|
@@ -24,6 +25,10 @@ class StaticPropsRegistry {
|
|
|
24
25
|
void removeObserver(Tag viewTag);
|
|
25
26
|
|
|
26
27
|
private:
|
|
28
|
+
/// registry_ is written on the JS thread (setViewStyle) and read on the UI
|
|
29
|
+
/// thread during interpolation and on the commit/mount thread during node
|
|
30
|
+
/// removals, so it needs its own guard. observers_ is JS-thread-only.
|
|
31
|
+
mutable std::mutex mutex_;
|
|
27
32
|
std::unordered_map<Tag, folly::dynamic> registry_;
|
|
28
33
|
std::unordered_map<Tag, PropsObserver> observers_;
|
|
29
34
|
|
|
@@ -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
|
|
|
@@ -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>
|
|
@@ -24,6 +25,31 @@ struct LayoutAnimation {
|
|
|
24
25
|
LayoutAnimation &operator=(const LayoutAnimation &other) = default;
|
|
25
26
|
};
|
|
26
27
|
|
|
28
|
+
#ifdef ANDROID
|
|
29
|
+
// Bookkeeping for animation starts that were scheduled onto the UI thread but
|
|
30
|
+
// haven't run yet — see `pendingStarts_` below.
|
|
31
|
+
struct PendingStart {
|
|
32
|
+
int count = 0;
|
|
33
|
+
uint64_t handle = 0;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Removes one pending start for the given tag and returns whether it was
|
|
37
|
+
// cancelled since it was scheduled (i.e. its handle is no longer current).
|
|
38
|
+
// Call under the proxy mutex.
|
|
39
|
+
inline bool
|
|
40
|
+
consumeIsCancelled(std::unordered_map<Tag, PendingStart> &pendingStarts, const Tag tag, const uint64_t handle) {
|
|
41
|
+
const auto it = pendingStarts.find(tag);
|
|
42
|
+
// every scheduled start keeps its entry alive until it is consumed —
|
|
43
|
+
// cancellations only bump the handle, they never erase
|
|
44
|
+
react_native_assert(it != pendingStarts.end() && "PendingStart not found");
|
|
45
|
+
const bool isCancelled = it->second.handle != handle;
|
|
46
|
+
if (--it->second.count == 0) {
|
|
47
|
+
pendingStarts.erase(it);
|
|
48
|
+
}
|
|
49
|
+
return isCancelled;
|
|
50
|
+
}
|
|
51
|
+
#endif
|
|
52
|
+
|
|
27
53
|
class LayoutAnimationsProxyCommon : public facebook::react::MountingOverrideDelegate {
|
|
28
54
|
public:
|
|
29
55
|
LayoutAnimationsProxyCommon(
|
|
@@ -75,6 +101,18 @@ class LayoutAnimationsProxyCommon : public facebook::react::MountingOverrideDele
|
|
|
75
101
|
|
|
76
102
|
void restoreOpacityInCaseOfFlakyEnteringAnimation(SurfaceId surfaceId) const;
|
|
77
103
|
|
|
104
|
+
// On Android pullTransaction can run on the JS thread, so animation starts
|
|
105
|
+
// are scheduled onto the UI thread. If `maybeCancelAnimation` is called between the
|
|
106
|
+
// start was scheduled and the lambda runs, it
|
|
107
|
+
// finds no `layoutAnimations_` entry to erase (the start lambda hasn't created it
|
|
108
|
+
// yet) and the cancellation is lost — the stale start would later
|
|
109
|
+
// "resurrect" the animation for a view whose Remove+Delete are already on
|
|
110
|
+
// their way to the mounting layer
|
|
111
|
+
// (https://github.com/software-mansion/react-native-reanimated/issues/7493).
|
|
112
|
+
|
|
113
|
+
// To work around this, we keep a separate `pendingStarts_` map that tracks scheduled starts by tag,
|
|
114
|
+
// with a generation counter to detect cancellations.
|
|
115
|
+
mutable std::unordered_map<Tag, PendingStart> pendingStarts_;
|
|
78
116
|
#endif
|
|
79
117
|
};
|
|
80
118
|
|
|
@@ -518,11 +518,10 @@ bool LayoutAnimationsProxy_Experimental::startAnimationsRecursively(
|
|
|
518
518
|
|
|
519
519
|
if (hasExitAnimation) {
|
|
520
520
|
node->state = ANIMATING;
|
|
521
|
-
startExitingAnimation(node);
|
|
522
521
|
lightNodes_[node->current.tag] = node;
|
|
522
|
+
startExitingAnimation(node);
|
|
523
523
|
} else {
|
|
524
|
-
|
|
525
|
-
// layoutAnimationsManager_->clearLayoutAnimationConfig(node->tag);
|
|
524
|
+
layoutAnimationsManager_->clearLayoutAnimationConfig(node->current.tag);
|
|
526
525
|
}
|
|
527
526
|
|
|
528
527
|
return wantAnimateExit;
|
|
@@ -534,6 +533,13 @@ void LayoutAnimationsProxy_Experimental::updateOngoingAnimationTarget(const int
|
|
|
534
533
|
}
|
|
535
534
|
|
|
536
535
|
void LayoutAnimationsProxy_Experimental::maybeCancelAnimation(const int tag) const {
|
|
536
|
+
#ifdef ANDROID
|
|
537
|
+
// also invalidate animation starts that are scheduled but haven't run yet,
|
|
538
|
+
// so they don't re-create the animation after this cancellation
|
|
539
|
+
if (const auto it = pendingStarts_.find(tag); it != pendingStarts_.end()) {
|
|
540
|
+
it->second.handle++;
|
|
541
|
+
}
|
|
542
|
+
#endif
|
|
537
543
|
if (!layoutAnimations_.contains(tag)) {
|
|
538
544
|
return;
|
|
539
545
|
}
|
|
@@ -678,9 +684,24 @@ void LayoutAnimationsProxy_Experimental::startEnteringAnimation(const std::share
|
|
|
678
684
|
const auto &parent = node->parent.lock();
|
|
679
685
|
react_native_assert(parent && "Parent node is nullptr");
|
|
680
686
|
const auto parentTag = parent->current.tag;
|
|
687
|
+
#ifdef ANDROID
|
|
688
|
+
const auto handle = pendingStarts_[newChildShadowView.tag].handle;
|
|
689
|
+
pendingStarts_[newChildShadowView.tag].count++;
|
|
690
|
+
#endif
|
|
681
691
|
|
|
682
692
|
scheduleOnUI(
|
|
683
|
-
uiScheduler_,
|
|
693
|
+
uiScheduler_,
|
|
694
|
+
[weakThis = weak_from_this(),
|
|
695
|
+
finalView,
|
|
696
|
+
currentView,
|
|
697
|
+
newChildShadowView,
|
|
698
|
+
parentTag,
|
|
699
|
+
opacity
|
|
700
|
+
#ifdef ANDROID
|
|
701
|
+
,
|
|
702
|
+
handle
|
|
703
|
+
#endif
|
|
704
|
+
]() {
|
|
684
705
|
auto strongThis = weakThis.lock();
|
|
685
706
|
if (!strongThis) {
|
|
686
707
|
return;
|
|
@@ -690,6 +711,12 @@ void LayoutAnimationsProxy_Experimental::startEnteringAnimation(const std::share
|
|
|
690
711
|
const auto tag = newChildShadowView.tag;
|
|
691
712
|
{
|
|
692
713
|
auto lock = std::unique_lock<std::recursive_mutex>(strongThis->mutex);
|
|
714
|
+
#ifdef ANDROID
|
|
715
|
+
if (consumeIsCancelled(strongThis->pendingStarts_, tag, handle)) {
|
|
716
|
+
// the view was removed before this start could run
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
#endif
|
|
693
720
|
strongThis->layoutAnimations_[tag] = {
|
|
694
721
|
.finalView = newChildShadowView,
|
|
695
722
|
.currentView = newChildShadowView,
|
|
@@ -723,38 +750,63 @@ void LayoutAnimationsProxy_Experimental::startExitingAnimation(const std::shared
|
|
|
723
750
|
const auto &parent = node->parent.lock();
|
|
724
751
|
react_native_assert(parent && "Parent node is nullptr");
|
|
725
752
|
const auto parentTag = parent->current.tag;
|
|
753
|
+
#ifdef ANDROID
|
|
754
|
+
const auto handle = pendingStarts_[tag].handle;
|
|
755
|
+
pendingStarts_[tag].count++;
|
|
756
|
+
#endif
|
|
726
757
|
|
|
727
|
-
scheduleOnUI(
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
758
|
+
scheduleOnUI(
|
|
759
|
+
uiScheduler_,
|
|
760
|
+
[weakThis = weak_from_this(),
|
|
761
|
+
tag,
|
|
762
|
+
parentTag,
|
|
763
|
+
oldChildShadowView,
|
|
764
|
+
surfaceId
|
|
765
|
+
#ifdef ANDROID
|
|
766
|
+
,
|
|
767
|
+
handle
|
|
768
|
+
#endif
|
|
769
|
+
]() {
|
|
770
|
+
auto strongThis = weakThis.lock();
|
|
771
|
+
if (!strongThis) {
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
732
774
|
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
775
|
+
auto oldView = oldChildShadowView;
|
|
776
|
+
Rect window{};
|
|
777
|
+
{
|
|
778
|
+
auto &mutex = strongThis->mutex;
|
|
779
|
+
auto lock = std::unique_lock<std::recursive_mutex>(mutex);
|
|
780
|
+
#ifdef ANDROID
|
|
781
|
+
if (consumeIsCancelled(strongThis->pendingStarts_, tag, handle)) {
|
|
782
|
+
// the view was removed (e.g. its subtree was force-ended by a screen
|
|
783
|
+
// pop) before this start could run — its Remove+Delete are already on
|
|
784
|
+
// their way to the mounting layer, so starting the animation now
|
|
785
|
+
// would emit updates for a view that's about to be deleted
|
|
786
|
+
strongThis->layoutAnimationsManager_->clearLayoutAnimationConfig(tag);
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
#endif
|
|
790
|
+
oldView = strongThis->maybeCreateLayoutAnimation(oldView, oldView, parentTag);
|
|
791
|
+
window = strongThis->surfaceManager.getWindow(surfaceId);
|
|
792
|
+
}
|
|
741
793
|
|
|
742
|
-
|
|
794
|
+
const Snapshot values(oldView, window);
|
|
743
795
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
796
|
+
auto &uiRuntime = strongThis->uiRuntime_;
|
|
797
|
+
const jsi::Object yogaValues(uiRuntime);
|
|
798
|
+
yogaValues.setProperty(uiRuntime, "currentOriginX", values.x);
|
|
799
|
+
yogaValues.setProperty(uiRuntime, "currentGlobalOriginX", values.x);
|
|
800
|
+
yogaValues.setProperty(uiRuntime, "currentOriginY", values.y);
|
|
801
|
+
yogaValues.setProperty(uiRuntime, "currentGlobalOriginY", values.y);
|
|
802
|
+
yogaValues.setProperty(uiRuntime, "currentWidth", values.width);
|
|
803
|
+
yogaValues.setProperty(uiRuntime, "currentHeight", values.height);
|
|
804
|
+
yogaValues.setProperty(uiRuntime, "windowWidth", values.windowWidth);
|
|
805
|
+
yogaValues.setProperty(uiRuntime, "windowHeight", values.windowHeight);
|
|
806
|
+
strongThis->layoutAnimationsManager_->startLayoutAnimation(
|
|
807
|
+
uiRuntime, tag, LayoutAnimationType::EXITING, yogaValues);
|
|
808
|
+
strongThis->layoutAnimationsManager_->clearLayoutAnimationConfig(tag);
|
|
809
|
+
});
|
|
758
810
|
}
|
|
759
811
|
|
|
760
812
|
void LayoutAnimationsProxy_Experimental::startLayoutAnimation(const std::shared_ptr<LightNode> &node) const {
|
|
@@ -765,9 +817,24 @@ void LayoutAnimationsProxy_Experimental::startLayoutAnimation(const std::shared_
|
|
|
765
817
|
const auto &parent = node->parent.lock();
|
|
766
818
|
react_native_assert(parent && "Parent node is nullptr");
|
|
767
819
|
const auto parentTag = parent->current.tag;
|
|
820
|
+
#ifdef ANDROID
|
|
821
|
+
const auto handle = pendingStarts_[tag].handle;
|
|
822
|
+
pendingStarts_[tag].count++;
|
|
823
|
+
#endif
|
|
768
824
|
|
|
769
825
|
scheduleOnUI(
|
|
770
|
-
uiScheduler_,
|
|
826
|
+
uiScheduler_,
|
|
827
|
+
[weakThis = weak_from_this(),
|
|
828
|
+
surfaceId,
|
|
829
|
+
oldChildShadowView,
|
|
830
|
+
newChildShadowView,
|
|
831
|
+
parentTag,
|
|
832
|
+
tag
|
|
833
|
+
#ifdef ANDROID
|
|
834
|
+
,
|
|
835
|
+
handle
|
|
836
|
+
#endif
|
|
837
|
+
]() {
|
|
771
838
|
auto strongThis = weakThis.lock();
|
|
772
839
|
if (!strongThis) {
|
|
773
840
|
return;
|
|
@@ -778,6 +845,12 @@ void LayoutAnimationsProxy_Experimental::startLayoutAnimation(const std::shared_
|
|
|
778
845
|
{
|
|
779
846
|
auto &mutex = strongThis->mutex;
|
|
780
847
|
auto lock = std::unique_lock<std::recursive_mutex>(mutex);
|
|
848
|
+
#ifdef ANDROID
|
|
849
|
+
if (consumeIsCancelled(strongThis->pendingStarts_, tag, handle)) {
|
|
850
|
+
// the view was removed before this start could run
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
#endif
|
|
781
854
|
oldView = strongThis->maybeCreateLayoutAnimation(oldView, newChildShadowView, parentTag);
|
|
782
855
|
window = strongThis->surfaceManager.getWindow(surfaceId);
|
|
783
856
|
}
|