react-native 0.84.0-nightly-20251205-95cc1e767 → 0.84.0-nightly-20251206-63b0aef13
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/Libraries/Animated/nodes/AnimatedProps.js +29 -3
- package/Libraries/Core/ReactNativeVersion.js +1 -1
- package/Libraries/NativeAnimation/RCTNativeAnimatedTurboModule.mm +7 -0
- package/Libraries/NativeAnimation/React-RCTAnimation.podspec +1 -0
- package/React/Base/RCTVersion.m +1 -1
- package/React/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec-generated.mm +7 -0
- package/React/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h +2 -0
- package/React/FBReactNativeSpec/FBReactNativeSpecJSI.h +10 -0
- package/ReactAndroid/gradle.properties +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.kt +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java +1 -10
- package/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewAccessibilityDelegate.kt +1 -0
- package/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.kt +42 -28
- package/ReactCommon/cxxreact/ReactNativeVersion.h +1 -1
- package/ReactCommon/jsi/jsi/jsi.cpp +2 -1
- package/ReactCommon/jsinspector-modern/HostAgent.cpp +2 -2
- package/ReactCommon/jsinspector-modern/InspectorInterfaces.cpp +10 -5
- package/ReactCommon/jsinspector-modern/InspectorInterfaces.h +2 -2
- package/ReactCommon/jsinspector-modern/TracingAgent.cpp +1 -1
- package/ReactCommon/react/renderer/animated/AnimatedModule.cpp +19 -1
- package/ReactCommon/react/renderer/animated/AnimatedModule.h +8 -0
- package/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp +83 -9
- package/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.h +6 -0
- package/ReactCommon/react/renderer/animated/drivers/FrameAnimationDriver.cpp +1 -0
- package/ReactCommon/react/renderer/animated/nodes/ColorAnimatedNode.cpp +0 -1
- package/ReactCommon/react/renderer/animated/nodes/PropsAnimatedNode.h +1 -0
- package/ReactCommon/react/renderer/animated/nodes/RoundAnimatedNode.cpp +1 -1
- package/ReactCommon/react/renderer/animated/nodes/ValueAnimatedNode.cpp +1 -1
- package/ReactCommon/react/renderer/animated/tests/AnimatedNodeTests.cpp +67 -0
- package/ReactCommon/react/renderer/animated/tests/AnimationDriverTests.cpp +59 -0
- package/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.cpp +81 -0
- package/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.h +83 -0
- package/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp +26 -15
- package/ReactCommon/react/renderer/animationbackend/AnimationBackend.h +7 -4
- package/ReactCommon/react/renderer/animationbackend/AnimationBackendCommitHook.cpp +69 -0
- package/ReactCommon/react/renderer/animationbackend/AnimationBackendCommitHook.h +32 -0
- package/ReactCommon/react/renderer/uimanager/UIManager.cpp +6 -0
- package/ReactCommon/react/renderer/uimanager/UIManagerAnimationBackend.h +1 -0
- package/package.json +9 -9
- package/sdks/hermes-engine/version.properties +1 -1
- package/src/private/animated/NativeAnimatedHelper.js +29 -1
- package/src/private/specs_DEPRECATED/modules/NativeAnimatedModule.js +4 -0
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
#include <react/renderer/animationbackend/AnimationBackend.h>
|
|
22
22
|
#endif
|
|
23
23
|
#include <react/renderer/core/ReactPrimitives.h>
|
|
24
|
+
#include <react/renderer/core/ShadowNode.h>
|
|
24
25
|
#include <react/renderer/uimanager/UIManagerAnimationBackend.h>
|
|
25
26
|
#include <chrono>
|
|
26
27
|
#include <memory>
|
|
@@ -101,6 +102,8 @@ class NativeAnimatedNodesManager {
|
|
|
101
102
|
|
|
102
103
|
void connectAnimatedNodeToView(Tag propsNodeTag, Tag viewTag) noexcept;
|
|
103
104
|
|
|
105
|
+
void connectAnimatedNodeToShadowNodeFamily(Tag propsNodeTag, std::shared_ptr<const ShadowNodeFamily> family) noexcept;
|
|
106
|
+
|
|
104
107
|
void disconnectAnimatedNodes(Tag parentTag, Tag childTag) noexcept;
|
|
105
108
|
|
|
106
109
|
void disconnectAnimatedNodeFromView(Tag propsNodeTag, Tag viewTag) noexcept;
|
|
@@ -258,6 +261,9 @@ class NativeAnimatedNodesManager {
|
|
|
258
261
|
std::unordered_map<Tag, folly::dynamic> updateViewProps_{};
|
|
259
262
|
std::unordered_map<Tag, folly::dynamic> updateViewPropsDirect_{};
|
|
260
263
|
|
|
264
|
+
mutable std::mutex tagToShadowNodeFamilyMutex_;
|
|
265
|
+
std::unordered_map<Tag, std::weak_ptr<const ShadowNodeFamily>> tagToShadowNodeFamily_{};
|
|
266
|
+
|
|
261
267
|
/*
|
|
262
268
|
* Sometimes a view is not longer connected to a PropsAnimatedNode, but
|
|
263
269
|
* NativeAnimated has previously changed the view's props via direct
|
|
@@ -43,6 +43,7 @@ void FrameAnimationDriver::updateConfig(folly::dynamic config) {
|
|
|
43
43
|
void FrameAnimationDriver::onConfigChanged() {
|
|
44
44
|
auto frames = config_["frames"];
|
|
45
45
|
react_native_assert(frames.type() == folly::dynamic::ARRAY);
|
|
46
|
+
frames_.clear();
|
|
46
47
|
for (const auto& frame : frames) {
|
|
47
48
|
auto frameValue = frame.asDouble();
|
|
48
49
|
frames_.push_back(frameValue);
|
|
@@ -22,7 +22,7 @@ RoundAnimatedNode::RoundAnimatedNode(
|
|
|
22
22
|
NativeAnimatedNodesManager& manager)
|
|
23
23
|
: ValueAnimatedNode(tag, config, manager),
|
|
24
24
|
inputNodeTag_(static_cast<Tag>(getConfig()["input"].asInt())),
|
|
25
|
-
nearest_(getConfig()["
|
|
25
|
+
nearest_(getConfig()["nearest"].asDouble()) {
|
|
26
26
|
react_native_assert(
|
|
27
27
|
nearest_ != 0 &&
|
|
28
28
|
"'nearest' cannot be 0 (can't round to the nearest multiple of 0)");
|
|
@@ -215,6 +215,73 @@ TEST_F(AnimatedNodeTests, DiffClampAnimatedNode) {
|
|
|
215
215
|
EXPECT_EQ(nodesManager_->getValue(diffClampTag), 1);
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
TEST_F(AnimatedNodeTests, RoundAnimatedNodeUsesNearestConfigKey) {
|
|
219
|
+
// This test verifies that RoundAnimatedNode reads the "nearest" config key
|
|
220
|
+
// for the rounding factor, not the "input" key.
|
|
221
|
+
initNodesManager();
|
|
222
|
+
|
|
223
|
+
auto rootTag = getNextRootViewTag();
|
|
224
|
+
|
|
225
|
+
auto valueTag = ++rootTag;
|
|
226
|
+
auto roundTag = ++rootTag;
|
|
227
|
+
|
|
228
|
+
nodesManager_->createAnimatedNode(
|
|
229
|
+
valueTag,
|
|
230
|
+
folly::dynamic::object("type", "value")("value", 7.3)("offset", 0));
|
|
231
|
+
|
|
232
|
+
// The round node should read "nearest" for the rounding factor (5.0),
|
|
233
|
+
// not "input" (which is the valueTag integer).
|
|
234
|
+
nodesManager_->createAnimatedNode(
|
|
235
|
+
roundTag,
|
|
236
|
+
folly::dynamic::object("type", "round")("input", valueTag)(
|
|
237
|
+
"nearest", 5.0));
|
|
238
|
+
nodesManager_->connectAnimatedNodes(valueTag, roundTag);
|
|
239
|
+
|
|
240
|
+
runAnimationFrame(0);
|
|
241
|
+
|
|
242
|
+
// 7.3 rounded to nearest 5.0 should be 5.0 (since round(7.3/5) * 5 = 1 * 5)
|
|
243
|
+
// If the bug existed (reading "input" instead of "nearest"), it would use
|
|
244
|
+
// the valueTag as the rounding factor, giving incorrect results.
|
|
245
|
+
EXPECT_DOUBLE_EQ(nodesManager_->getValue(roundTag).value(), 5.0);
|
|
246
|
+
|
|
247
|
+
// Test another value to ensure rounding works correctly
|
|
248
|
+
nodesManager_->setAnimatedNodeValue(valueTag, 12.6);
|
|
249
|
+
runAnimationFrame(0);
|
|
250
|
+
|
|
251
|
+
// 12.6 rounded to nearest 5.0 should be 15.0 (since round(12.6/5) * 5 = 3 *
|
|
252
|
+
// 5)
|
|
253
|
+
EXPECT_DOUBLE_EQ(nodesManager_->getValue(roundTag).value(), 15.0);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
TEST_F(AnimatedNodeTests, SetOffsetReturnsFalseWhenUnchanged) {
|
|
257
|
+
// This test verifies that setAnimatedNodeOffset doesn't trigger unnecessary
|
|
258
|
+
// updates when the offset value hasn't changed.
|
|
259
|
+
initNodesManager();
|
|
260
|
+
|
|
261
|
+
auto rootTag = getNextRootViewTag();
|
|
262
|
+
auto valueTag = ++rootTag;
|
|
263
|
+
|
|
264
|
+
nodesManager_->createAnimatedNode(
|
|
265
|
+
valueTag,
|
|
266
|
+
folly::dynamic::object("type", "value")("value", 10)("offset", 0));
|
|
267
|
+
|
|
268
|
+
runAnimationFrame(0);
|
|
269
|
+
EXPECT_EQ(nodeNeedsUpdate(valueTag), false);
|
|
270
|
+
|
|
271
|
+
// First setOffset should mark the node as needing update
|
|
272
|
+
nodesManager_->setAnimatedNodeOffset(valueTag, 5);
|
|
273
|
+
EXPECT_EQ(nodeNeedsUpdate(valueTag), true);
|
|
274
|
+
EXPECT_EQ(nodesManager_->getValue(valueTag), 15); // 10 + 5
|
|
275
|
+
|
|
276
|
+
runAnimationFrame(0);
|
|
277
|
+
EXPECT_EQ(nodeNeedsUpdate(valueTag), false);
|
|
278
|
+
|
|
279
|
+
// Setting the same offset again should NOT mark the node as needing update
|
|
280
|
+
nodesManager_->setAnimatedNodeOffset(valueTag, 5);
|
|
281
|
+
EXPECT_EQ(nodeNeedsUpdate(valueTag), false); // No change, no update needed
|
|
282
|
+
EXPECT_EQ(nodesManager_->getValue(valueTag), 15); // Still 10 + 5
|
|
283
|
+
}
|
|
284
|
+
|
|
218
285
|
TEST_F(AnimatedNodeTests, ObjectAnimatedNode) {
|
|
219
286
|
initNodesManager();
|
|
220
287
|
|
|
@@ -58,4 +58,63 @@ TEST_F(AnimationDriverTests, framesAnimation) {
|
|
|
58
58
|
EXPECT_EQ(round(nodesManager_->getValue(valueNodeTag).value()), toValue);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
TEST_F(AnimationDriverTests, framesAnimationReconfigurationClearsFrames) {
|
|
62
|
+
// This test verifies that when an animation is reconfigured via updateConfig,
|
|
63
|
+
// the frames array is cleared before adding new frames. Without clearing,
|
|
64
|
+
// frames would accumulate and cause incorrect animation behavior.
|
|
65
|
+
initNodesManager();
|
|
66
|
+
|
|
67
|
+
auto rootTag = getNextRootViewTag();
|
|
68
|
+
|
|
69
|
+
auto valueNodeTag = ++rootTag;
|
|
70
|
+
nodesManager_->createAnimatedNode(
|
|
71
|
+
valueNodeTag,
|
|
72
|
+
folly::dynamic::object("type", "value")("value", 0)("offset", 0));
|
|
73
|
+
|
|
74
|
+
const auto animationId = 1;
|
|
75
|
+
// First animation: 5 frames from 0 to 100
|
|
76
|
+
const auto frames1 = folly::dynamic::array(0.0f, 0.25f, 0.5f, 0.75f, 1.0f);
|
|
77
|
+
const auto toValue1 = 100;
|
|
78
|
+
nodesManager_->startAnimatingNode(
|
|
79
|
+
animationId,
|
|
80
|
+
valueNodeTag,
|
|
81
|
+
folly::dynamic::object("type", "frames")("frames", frames1)(
|
|
82
|
+
"toValue", toValue1),
|
|
83
|
+
std::nullopt);
|
|
84
|
+
|
|
85
|
+
const double startTimeInTick = 12345;
|
|
86
|
+
|
|
87
|
+
// Run first frame
|
|
88
|
+
runAnimationFrame(startTimeInTick);
|
|
89
|
+
EXPECT_EQ(round(nodesManager_->getValue(valueNodeTag).value()), 0);
|
|
90
|
+
|
|
91
|
+
// Reconfigure the same animation (same animationId) with new frames
|
|
92
|
+
// This triggers updateConfig on the existing FrameAnimationDriver
|
|
93
|
+
const auto frames2 = folly::dynamic::array(0.0f, 0.5f, 1.0f);
|
|
94
|
+
const auto toValue2 = 200;
|
|
95
|
+
nodesManager_->startAnimatingNode(
|
|
96
|
+
animationId,
|
|
97
|
+
valueNodeTag,
|
|
98
|
+
folly::dynamic::object("type", "frames")("frames", frames2)(
|
|
99
|
+
"toValue", toValue2),
|
|
100
|
+
std::nullopt);
|
|
101
|
+
|
|
102
|
+
// Reset animation timing
|
|
103
|
+
const double newStartTimeInTick = 20000;
|
|
104
|
+
|
|
105
|
+
// Run animation at halfway point (1 frame into 3-frame animation)
|
|
106
|
+
runAnimationFrame(newStartTimeInTick);
|
|
107
|
+
runAnimationFrame(newStartTimeInTick + SingleFrameIntervalMs * 1);
|
|
108
|
+
|
|
109
|
+
// At frame 1 of 3 frames (50% progress), value should be approximately:
|
|
110
|
+
// startValue (0) + 0.5 * (toValue2 - startValue) = 0 + 0.5 * 200 = 100
|
|
111
|
+
// If frames accumulated (5 + 3 = 8 frames), we'd be at wrong position
|
|
112
|
+
// Use ceil rounding so 100.00x becomes 100.01
|
|
113
|
+
EXPECT_EQ(round(nodesManager_->getValue(valueNodeTag).value()), 100.01);
|
|
114
|
+
|
|
115
|
+
// Complete the animation
|
|
116
|
+
runAnimationFrame(newStartTimeInTick + SingleFrameIntervalMs * 2);
|
|
117
|
+
EXPECT_EQ(round(nodesManager_->getValue(valueNodeTag).value()), toValue2);
|
|
118
|
+
}
|
|
119
|
+
|
|
61
120
|
} // namespace facebook::react
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#include "AnimatedPropsRegistry.h"
|
|
9
|
+
#include <react/renderer/core/PropsParserContext.h>
|
|
10
|
+
#include "AnimatedProps.h"
|
|
11
|
+
|
|
12
|
+
namespace facebook::react {
|
|
13
|
+
|
|
14
|
+
void AnimatedPropsRegistry::update(
|
|
15
|
+
const std::unordered_map<SurfaceId, SurfaceUpdates>& surfaceUpdates) {
|
|
16
|
+
auto lock = std::lock_guard(mutex_);
|
|
17
|
+
for (const auto& [surfaceId, updates] : surfaceUpdates) {
|
|
18
|
+
auto& surfaceContext = surfaceContexts_[surfaceId];
|
|
19
|
+
auto& pendingMap = surfaceContext.pendingMap;
|
|
20
|
+
auto& pendingFamilies = surfaceContext.pendingFamilies;
|
|
21
|
+
|
|
22
|
+
auto& updatesMap = updates.propsMap;
|
|
23
|
+
auto& updatesFamilies = updates.families;
|
|
24
|
+
|
|
25
|
+
for (auto& family : updatesFamilies) {
|
|
26
|
+
pendingFamilies.insert(family);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
for (auto& [tag, animatedProps] : updatesMap) {
|
|
30
|
+
auto it = pendingMap.find(tag);
|
|
31
|
+
if (it == pendingMap.end()) {
|
|
32
|
+
it = pendingMap.insert_or_assign(tag, std::make_unique<PropsSnapshot>())
|
|
33
|
+
.first;
|
|
34
|
+
}
|
|
35
|
+
auto& snapshot = it->second;
|
|
36
|
+
auto& viewProps = snapshot->props;
|
|
37
|
+
|
|
38
|
+
for (const auto& animatedProp : animatedProps.props) {
|
|
39
|
+
snapshot->propNames.insert(animatedProp->propName);
|
|
40
|
+
cloneProp(viewProps, *animatedProp);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
std::pair<std::unordered_set<const ShadowNodeFamily*>&, SnapshotMap&>
|
|
47
|
+
AnimatedPropsRegistry::getMap(SurfaceId surfaceId) {
|
|
48
|
+
auto lock = std::lock_guard(mutex_);
|
|
49
|
+
auto& [pendingMap, map, pendingFamilies, families] =
|
|
50
|
+
surfaceContexts_[surfaceId];
|
|
51
|
+
|
|
52
|
+
for (auto& family : pendingFamilies) {
|
|
53
|
+
families.insert(family);
|
|
54
|
+
}
|
|
55
|
+
for (auto& [tag, propsSnapshot] : pendingMap) {
|
|
56
|
+
auto currentIt = map.find(tag);
|
|
57
|
+
if (currentIt == map.end()) {
|
|
58
|
+
map.insert_or_assign(tag, std::move(propsSnapshot));
|
|
59
|
+
} else {
|
|
60
|
+
auto& currentSnapshot = currentIt->second;
|
|
61
|
+
for (auto& propName : propsSnapshot->propNames) {
|
|
62
|
+
currentSnapshot->propNames.insert(propName);
|
|
63
|
+
updateProp(propName, currentSnapshot->props, *propsSnapshot);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
pendingMap.clear();
|
|
68
|
+
pendingFamilies.clear();
|
|
69
|
+
|
|
70
|
+
return {families, map};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
void AnimatedPropsRegistry::clear(SurfaceId surfaceId) {
|
|
74
|
+
auto lock = std::lock_guard(mutex_);
|
|
75
|
+
|
|
76
|
+
auto& surfaceContext = surfaceContexts_[surfaceId];
|
|
77
|
+
surfaceContext.families.clear();
|
|
78
|
+
surfaceContext.map.clear();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
} // namespace facebook::react
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include <folly/dynamic.h>
|
|
11
|
+
#include <react/renderer/components/view/BaseViewProps.h>
|
|
12
|
+
#include <react/renderer/core/ReactPrimitives.h>
|
|
13
|
+
#include <react/renderer/uimanager/UIManager.h>
|
|
14
|
+
#include <react/renderer/uimanager/UIManagerCommitHook.h>
|
|
15
|
+
#include "AnimatedProps.h"
|
|
16
|
+
|
|
17
|
+
namespace facebook::react {
|
|
18
|
+
|
|
19
|
+
struct PropsSnapshot {
|
|
20
|
+
BaseViewProps props;
|
|
21
|
+
std::unordered_set<PropName> propNames;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
struct SurfaceContext {
|
|
25
|
+
std::unordered_map<Tag, std::unique_ptr<PropsSnapshot>> pendingMap, map;
|
|
26
|
+
std::unordered_set<const ShadowNodeFamily *> pendingFamilies, families;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
struct SurfaceUpdates {
|
|
30
|
+
std::unordered_set<const ShadowNodeFamily *> families;
|
|
31
|
+
std::unordered_map<Tag, AnimatedProps> propsMap;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
using SnapshotMap = std::unordered_map<Tag, std::unique_ptr<PropsSnapshot>>;
|
|
35
|
+
|
|
36
|
+
class AnimatedPropsRegistry {
|
|
37
|
+
public:
|
|
38
|
+
void update(const std::unordered_map<SurfaceId, SurfaceUpdates> &surfaceUpdates);
|
|
39
|
+
void clear(SurfaceId surfaceId);
|
|
40
|
+
std::pair<std::unordered_set<const ShadowNodeFamily *> &, SnapshotMap &> getMap(SurfaceId surfaceId);
|
|
41
|
+
|
|
42
|
+
private:
|
|
43
|
+
std::unordered_map<SurfaceId, SurfaceContext> surfaceContexts_;
|
|
44
|
+
std::mutex mutex_;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
inline void updateProp(const PropName propName, BaseViewProps &viewProps, const PropsSnapshot &snapshot)
|
|
48
|
+
{
|
|
49
|
+
switch (propName) {
|
|
50
|
+
case OPACITY:
|
|
51
|
+
viewProps.opacity = snapshot.props.opacity;
|
|
52
|
+
break;
|
|
53
|
+
|
|
54
|
+
case WIDTH:
|
|
55
|
+
viewProps.yogaStyle.setDimension(
|
|
56
|
+
yoga::Dimension::Width, snapshot.props.yogaStyle.dimension(yoga::Dimension::Width));
|
|
57
|
+
break;
|
|
58
|
+
|
|
59
|
+
case HEIGHT: {
|
|
60
|
+
auto d = snapshot.props.yogaStyle.dimension(yoga::Dimension::Height);
|
|
61
|
+
viewProps.yogaStyle.setDimension(yoga::Dimension::Height, d);
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
case TRANSFORM:
|
|
66
|
+
viewProps.transform = snapshot.props.transform;
|
|
67
|
+
break;
|
|
68
|
+
|
|
69
|
+
case BORDER_RADII:
|
|
70
|
+
viewProps.borderRadii = snapshot.props.borderRadii;
|
|
71
|
+
break;
|
|
72
|
+
|
|
73
|
+
case FLEX:
|
|
74
|
+
viewProps.yogaStyle.setFlex(snapshot.props.yogaStyle.flex());
|
|
75
|
+
break;
|
|
76
|
+
|
|
77
|
+
case BACKGROUND_COLOR:
|
|
78
|
+
viewProps.backgroundColor = snapshot.props.backgroundColor;
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
} // namespace facebook::react
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
#include <react/renderer/animationbackend/AnimatedPropsSerializer.h>
|
|
10
10
|
#include <react/renderer/graphics/Color.h>
|
|
11
11
|
#include <chrono>
|
|
12
|
+
#include "AnimatedPropsRegistry.h"
|
|
12
13
|
|
|
13
14
|
namespace facebook::react {
|
|
14
15
|
|
|
@@ -70,12 +71,14 @@ AnimationBackend::AnimationBackend(
|
|
|
70
71
|
stopOnRenderCallback_(std::move(stopOnRenderCallback)),
|
|
71
72
|
directManipulationCallback_(std::move(directManipulationCallback)),
|
|
72
73
|
fabricCommitCallback_(std::move(fabricCommitCallback)),
|
|
73
|
-
|
|
74
|
+
animatedPropsRegistry_(std::make_shared<AnimatedPropsRegistry>()),
|
|
75
|
+
uiManager_(uiManager),
|
|
76
|
+
commitHook_(uiManager, animatedPropsRegistry_) {}
|
|
74
77
|
|
|
75
78
|
void AnimationBackend::onAnimationFrame(double timestamp) {
|
|
76
|
-
std::unordered_map<Tag, AnimatedProps>
|
|
77
|
-
std::unordered_map<SurfaceId,
|
|
78
|
-
|
|
79
|
+
std::unordered_map<Tag, AnimatedProps> synchronousUpdates;
|
|
80
|
+
std::unordered_map<SurfaceId, SurfaceUpdates> surfaceUpdates;
|
|
81
|
+
|
|
79
82
|
bool hasAnyLayoutUpdates = false;
|
|
80
83
|
for (auto& callback : callbacks) {
|
|
81
84
|
auto muatations = callback(static_cast<float>(timestamp));
|
|
@@ -83,22 +86,28 @@ void AnimationBackend::onAnimationFrame(double timestamp) {
|
|
|
83
86
|
hasAnyLayoutUpdates |= mutationHasLayoutUpdates(mutation);
|
|
84
87
|
const auto family = mutation.family;
|
|
85
88
|
if (family != nullptr) {
|
|
86
|
-
|
|
89
|
+
auto& [families, updates] = surfaceUpdates[family->getSurfaceId()];
|
|
90
|
+
families.insert(family.get());
|
|
91
|
+
updates[mutation.tag] = std::move(mutation.props);
|
|
92
|
+
} else {
|
|
93
|
+
synchronousUpdates[mutation.tag] = std::move(mutation.props);
|
|
87
94
|
}
|
|
88
|
-
updates[mutation.tag] = std::move(mutation.props);
|
|
89
95
|
}
|
|
90
96
|
}
|
|
91
97
|
|
|
98
|
+
animatedPropsRegistry_->update(surfaceUpdates);
|
|
99
|
+
|
|
92
100
|
if (hasAnyLayoutUpdates) {
|
|
93
|
-
commitUpdates(
|
|
101
|
+
commitUpdates(surfaceUpdates);
|
|
94
102
|
} else {
|
|
95
|
-
synchronouslyUpdateProps(
|
|
103
|
+
synchronouslyUpdateProps(synchronousUpdates);
|
|
96
104
|
}
|
|
97
105
|
}
|
|
98
106
|
|
|
99
107
|
void AnimationBackend::start(const Callback& callback, bool isAsync) {
|
|
100
108
|
callbacks.push_back(callback);
|
|
101
|
-
// TODO: startOnRenderCallback_ should provide the timestamp from the
|
|
109
|
+
// TODO: startOnRenderCallback_ should provide the timestamp from the
|
|
110
|
+
// platform
|
|
102
111
|
if (startOnRenderCallback_) {
|
|
103
112
|
startOnRenderCallback_(
|
|
104
113
|
[this]() {
|
|
@@ -117,13 +126,11 @@ void AnimationBackend::stop(bool isAsync) {
|
|
|
117
126
|
}
|
|
118
127
|
|
|
119
128
|
void AnimationBackend::commitUpdates(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
std::unordered_set<const ShadowNodeFamily*>>& surfaceToFamilies,
|
|
123
|
-
std::unordered_map<Tag, AnimatedProps>& updates) {
|
|
124
|
-
for (const auto& surfaceEntry : surfaceToFamilies) {
|
|
129
|
+
std::unordered_map<SurfaceId, SurfaceUpdates>& surfaceUpdates) {
|
|
130
|
+
for (auto& surfaceEntry : surfaceUpdates) {
|
|
125
131
|
const auto& surfaceId = surfaceEntry.first;
|
|
126
|
-
const auto& surfaceFamilies = surfaceEntry.second;
|
|
132
|
+
const auto& surfaceFamilies = surfaceEntry.second.families;
|
|
133
|
+
auto& updates = surfaceEntry.second.propsMap;
|
|
127
134
|
uiManager_->getShadowTreeRegistry().visit(
|
|
128
135
|
surfaceId, [&surfaceFamilies, &updates](const ShadowTree& shadowTree) {
|
|
129
136
|
shadowTree.commit(
|
|
@@ -165,4 +172,8 @@ void AnimationBackend::synchronouslyUpdateProps(
|
|
|
165
172
|
}
|
|
166
173
|
}
|
|
167
174
|
|
|
175
|
+
void AnimationBackend::clearRegistry(SurfaceId surfaceId) {
|
|
176
|
+
animatedPropsRegistry_->clear(surfaceId);
|
|
177
|
+
}
|
|
178
|
+
|
|
168
179
|
} // namespace facebook::react
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
#include <vector>
|
|
16
16
|
#include "AnimatedProps.h"
|
|
17
17
|
#include "AnimatedPropsBuilder.h"
|
|
18
|
+
#include "AnimatedPropsRegistry.h"
|
|
19
|
+
#include "AnimationBackendCommitHook.h"
|
|
18
20
|
|
|
19
21
|
namespace facebook::react {
|
|
20
22
|
|
|
@@ -32,7 +34,7 @@ class UIManagerNativeAnimatedDelegateBackendImpl : public UIManagerNativeAnimate
|
|
|
32
34
|
|
|
33
35
|
struct AnimationMutation {
|
|
34
36
|
Tag tag;
|
|
35
|
-
const ShadowNodeFamily
|
|
37
|
+
std::shared_ptr<const ShadowNodeFamily> family;
|
|
36
38
|
AnimatedProps props;
|
|
37
39
|
};
|
|
38
40
|
|
|
@@ -51,7 +53,9 @@ class AnimationBackend : public UIManagerAnimationBackend {
|
|
|
51
53
|
const StopOnRenderCallback stopOnRenderCallback_;
|
|
52
54
|
const DirectManipulationCallback directManipulationCallback_;
|
|
53
55
|
const FabricCommitCallback fabricCommitCallback_;
|
|
56
|
+
std::shared_ptr<AnimatedPropsRegistry> animatedPropsRegistry_;
|
|
54
57
|
UIManager *uiManager_;
|
|
58
|
+
AnimationBackendCommitHook commitHook_;
|
|
55
59
|
|
|
56
60
|
AnimationBackend(
|
|
57
61
|
StartOnRenderCallback &&startOnRenderCallback,
|
|
@@ -59,10 +63,9 @@ class AnimationBackend : public UIManagerAnimationBackend {
|
|
|
59
63
|
DirectManipulationCallback &&directManipulationCallback,
|
|
60
64
|
FabricCommitCallback &&fabricCommitCallback,
|
|
61
65
|
UIManager *uiManager);
|
|
62
|
-
void commitUpdates(
|
|
63
|
-
const std::unordered_map<SurfaceId, std::unordered_set<const ShadowNodeFamily *>> &surfaceToFamilies,
|
|
64
|
-
std::unordered_map<Tag, AnimatedProps> &updates);
|
|
66
|
+
void commitUpdates(std::unordered_map<SurfaceId, SurfaceUpdates> &surfaceUpdates);
|
|
65
67
|
void synchronouslyUpdateProps(const std::unordered_map<Tag, AnimatedProps> &updates);
|
|
68
|
+
void clearRegistry(SurfaceId surfaceId) override;
|
|
66
69
|
|
|
67
70
|
void onAnimationFrame(double timestamp) override;
|
|
68
71
|
void start(const Callback &callback, bool isAsync);
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#include <react/renderer/animationbackend/AnimationBackendCommitHook.h>
|
|
9
|
+
|
|
10
|
+
namespace facebook::react {
|
|
11
|
+
|
|
12
|
+
AnimationBackendCommitHook::AnimationBackendCommitHook(
|
|
13
|
+
UIManager* uiManager,
|
|
14
|
+
std::shared_ptr<AnimatedPropsRegistry> animatedPropsRegistry)
|
|
15
|
+
: animatedPropsRegistry_(std::move(animatedPropsRegistry)) {
|
|
16
|
+
uiManager->registerCommitHook(*this);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
RootShadowNode::Unshared AnimationBackendCommitHook::shadowTreeWillCommit(
|
|
20
|
+
const ShadowTree& shadowTree,
|
|
21
|
+
const RootShadowNode::Shared& oldRootShadowNode,
|
|
22
|
+
const RootShadowNode::Unshared& newRootShadowNode,
|
|
23
|
+
const ShadowTreeCommitOptions& commitOptions) noexcept {
|
|
24
|
+
if (commitOptions.source != ShadowTreeCommitSource::React) {
|
|
25
|
+
return newRootShadowNode;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const auto& res = animatedPropsRegistry_->getMap(shadowTree.getSurfaceId());
|
|
29
|
+
auto& surfaceFamilies = res.first;
|
|
30
|
+
auto& updates = res.second;
|
|
31
|
+
|
|
32
|
+
if (surfaceFamilies.empty()) {
|
|
33
|
+
return newRootShadowNode;
|
|
34
|
+
}
|
|
35
|
+
return std::static_pointer_cast<RootShadowNode>(
|
|
36
|
+
newRootShadowNode->cloneMultiple(
|
|
37
|
+
surfaceFamilies,
|
|
38
|
+
[&surfaceFamilies, &updates](
|
|
39
|
+
const ShadowNode& shadowNode,
|
|
40
|
+
const ShadowNodeFragment& fragment) {
|
|
41
|
+
auto newProps = ShadowNodeFragment::propsPlaceholder();
|
|
42
|
+
std::shared_ptr<BaseViewProps> viewProps = nullptr;
|
|
43
|
+
if (surfaceFamilies.contains(&shadowNode.getFamily()) &&
|
|
44
|
+
updates.contains(shadowNode.getTag())) {
|
|
45
|
+
auto& snapshot = updates.at(shadowNode.getTag());
|
|
46
|
+
if (!snapshot->propNames.empty()) {
|
|
47
|
+
PropsParserContext propsParserContext{
|
|
48
|
+
shadowNode.getSurfaceId(),
|
|
49
|
+
*shadowNode.getContextContainer()};
|
|
50
|
+
|
|
51
|
+
newProps = shadowNode.getComponentDescriptor().cloneProps(
|
|
52
|
+
propsParserContext, shadowNode.getProps(), {});
|
|
53
|
+
viewProps = std::const_pointer_cast<BaseViewProps>(
|
|
54
|
+
std::static_pointer_cast<const BaseViewProps>(newProps));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
for (const auto& propName : snapshot->propNames) {
|
|
58
|
+
updateProp(propName, *viewProps, *snapshot);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return shadowNode.clone(
|
|
62
|
+
{.props = newProps,
|
|
63
|
+
.children = fragment.children,
|
|
64
|
+
.state = shadowNode.getState(),
|
|
65
|
+
.runtimeShadowNodeReference = true});
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
} // namespace facebook::react
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include <folly/dynamic.h>
|
|
11
|
+
#include <react/renderer/core/ReactPrimitives.h>
|
|
12
|
+
#include <react/renderer/uimanager/UIManager.h>
|
|
13
|
+
#include <react/renderer/uimanager/UIManagerCommitHook.h>
|
|
14
|
+
#include "AnimatedPropsRegistry.h"
|
|
15
|
+
|
|
16
|
+
namespace facebook::react {
|
|
17
|
+
|
|
18
|
+
class AnimationBackendCommitHook : public UIManagerCommitHook {
|
|
19
|
+
std::shared_ptr<AnimatedPropsRegistry> animatedPropsRegistry_;
|
|
20
|
+
|
|
21
|
+
public:
|
|
22
|
+
AnimationBackendCommitHook(UIManager *uiManager, std::shared_ptr<AnimatedPropsRegistry> animatedPropsRegistry);
|
|
23
|
+
RootShadowNode::Unshared shadowTreeWillCommit(
|
|
24
|
+
const ShadowTree &shadowTree,
|
|
25
|
+
const RootShadowNode::Shared &oldRootShadowNode,
|
|
26
|
+
const RootShadowNode::Unshared &newRootShadowNode,
|
|
27
|
+
const ShadowTreeCommitOptions &commitOptions) noexcept override;
|
|
28
|
+
void commitHookWasRegistered(const UIManager &uiManager) noexcept override {}
|
|
29
|
+
void commitHookWasUnregistered(const UIManager &uiManager) noexcept override {}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
} // namespace facebook::react
|
|
@@ -205,6 +205,12 @@ void UIManager::completeSurface(
|
|
|
205
205
|
// after we commit a specific one.
|
|
206
206
|
lazyShadowTreeRevisionConsistencyManager_->updateCurrentRevision(
|
|
207
207
|
surfaceId, shadowTree.getCurrentRevision().rootShadowNode);
|
|
208
|
+
|
|
209
|
+
if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) {
|
|
210
|
+
if (auto animationBackend = animationBackend_.lock()) {
|
|
211
|
+
animationBackend->clearRegistry(surfaceId);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
208
214
|
}
|
|
209
215
|
});
|
|
210
216
|
}
|
|
@@ -19,6 +19,7 @@ class UIManagerAnimationBackend {
|
|
|
19
19
|
virtual void onAnimationFrame(double timestamp) = 0;
|
|
20
20
|
// TODO: T240293839 Move over start() function and mutation types
|
|
21
21
|
virtual void stop(bool isAsync) = 0;
|
|
22
|
+
virtual void clearRegistry(SurfaceId surfaceId) = 0;
|
|
22
23
|
};
|
|
23
24
|
|
|
24
25
|
} // namespace facebook::react
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native",
|
|
3
|
-
"version": "0.84.0-nightly-
|
|
3
|
+
"version": "0.84.0-nightly-20251206-63b0aef13",
|
|
4
4
|
"description": "A framework for building native apps using React",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -160,13 +160,13 @@
|
|
|
160
160
|
},
|
|
161
161
|
"dependencies": {
|
|
162
162
|
"@jest/create-cache-key-function": "^29.7.0",
|
|
163
|
-
"@react-native/assets-registry": "0.84.0-nightly-
|
|
164
|
-
"@react-native/codegen": "0.84.0-nightly-
|
|
165
|
-
"@react-native/community-cli-plugin": "0.84.0-nightly-
|
|
166
|
-
"@react-native/gradle-plugin": "0.84.0-nightly-
|
|
167
|
-
"@react-native/js-polyfills": "0.84.0-nightly-
|
|
168
|
-
"@react-native/normalize-colors": "0.84.0-nightly-
|
|
169
|
-
"@react-native/virtualized-lists": "0.84.0-nightly-
|
|
163
|
+
"@react-native/assets-registry": "0.84.0-nightly-20251206-63b0aef13",
|
|
164
|
+
"@react-native/codegen": "0.84.0-nightly-20251206-63b0aef13",
|
|
165
|
+
"@react-native/community-cli-plugin": "0.84.0-nightly-20251206-63b0aef13",
|
|
166
|
+
"@react-native/gradle-plugin": "0.84.0-nightly-20251206-63b0aef13",
|
|
167
|
+
"@react-native/js-polyfills": "0.84.0-nightly-20251206-63b0aef13",
|
|
168
|
+
"@react-native/normalize-colors": "0.84.0-nightly-20251206-63b0aef13",
|
|
169
|
+
"@react-native/virtualized-lists": "0.84.0-nightly-20251206-63b0aef13",
|
|
170
170
|
"abort-controller": "^3.0.0",
|
|
171
171
|
"anser": "^1.4.9",
|
|
172
172
|
"ansi-regex": "^5.0.0",
|
|
@@ -175,7 +175,7 @@
|
|
|
175
175
|
"base64-js": "^1.5.1",
|
|
176
176
|
"commander": "^12.0.0",
|
|
177
177
|
"flow-enums-runtime": "^0.0.6",
|
|
178
|
-
"hermes-compiler": "0.14.0-commitly-
|
|
178
|
+
"hermes-compiler": "0.14.0-commitly-202512051838-1e8c447f2",
|
|
179
179
|
"invariant": "^2.2.4",
|
|
180
180
|
"jest-environment-node": "^29.7.0",
|
|
181
181
|
"memoize-one": "^5.0.0",
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
HERMES_VERSION_NAME=0.14.0-commitly-
|
|
1
|
+
HERMES_VERSION_NAME=0.14.0-commitly-202512051838-1e8c447f2
|
|
2
2
|
HERMES_V1_VERSION_NAME=250829098.0.4
|