Skip to content

Commit 9aa047f

Browse files
committed
Prepare platform transition routing for more properties
1 parent e937206 commit 9aa047f

18 files changed

Lines changed: 392 additions & 208 deletions

packages/react-native-reanimated/Common/cpp/reanimated/CSS/common/values/CSSColor.cpp

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include <reanimated/CSS/common/values/CSSColor.h>
22
#include <reanimated/CSS/svg/values/SVGBrush.h>
3+
#include <reanimated/CSS/utils/props.h>
34

45
#include <utility>
56

@@ -17,24 +18,7 @@ CSSColorBase<TColorType, TDerived>::CSSColorBase(TColorType colorType) : channel
1718

1819
template <ColorTypeEnum TColorType, typename TDerived>
1920
CSSColorBase<TColorType, TDerived>::CSSColorBase(int64_t numberValue)
20-
: channels{0, 0, 0, 0}, colorType(TColorType::Rgba) {
21-
uint32_t color;
22-
// On Android, colors are represented as signed 32-bit integers. In JS, we use
23-
// a bitwise operation (normalizedColor = normalizedColor | 0x0) to ensure the
24-
// value is treated as a signed int, causing numbers above 2^31 to become
25-
// negative. To correctly interpret these in C++, we cast negative values to
26-
// int32_t to preserve their bit pattern, then assign to uint32_t. This wraps
27-
// the bits (modulo 2^32), effectively reversing the JS-side bit shift.
28-
if (numberValue < 0) {
29-
color = static_cast<int32_t>(numberValue);
30-
} else {
31-
color = static_cast<uint32_t>(numberValue);
32-
}
33-
channels[0] = (color >> 16) & 0xFF; // Red
34-
channels[1] = (color >> 8) & 0xFF; // Green
35-
channels[2] = color & 0xFF; // Blue
36-
channels[3] = (color >> 24) & 0xFF; // Alpha
37-
}
21+
: channels(extractColorChannels(numberValue)), colorType(TColorType::Rgba) {}
3822

3923
template <ColorTypeEnum TColorType, typename TDerived>
4024
CSSColorBase<TColorType, TDerived>::CSSColorBase(bool value)

packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/transition/CSSPlatformTransition.cpp

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,22 @@ CSSPlatformTransition::CSSPlatformTransition(
1212
const std::shared_ptr<CSSPlatformTransitionProxy> &proxy)
1313
: viewTag_(viewTag), proxy_(proxy) {}
1414

15-
void CSSPlatformTransition::run(jsi::Runtime &rt, const CSSPlatformTransitionConfig &config, const double timestamp) {
15+
void CSSPlatformTransition::run(const CSSPlatformTransitionConfig &config, const double timestamp) {
1616
for (const auto &entry : config.changedProperties) {
17-
runEntry(rt, entry, timestamp);
17+
applyEntry(entry.propertyName, entry.fromValue, entry.toValue, entry.settings, timestamp);
1818
}
1919

2020
for (const auto &propertyName : config.removedProperties) {
2121
cancel(propertyName);
2222
}
2323
}
2424

25-
void CSSPlatformTransition::run(const PropertyValueDynamicDiffsMap &propertiesDiffs, const double timestamp) {
25+
void CSSPlatformTransition::run(const ParsedPlatformDiffs &propertiesDiffs, const double timestamp) {
2626
for (const auto &[propertyName, propertyDiff] : propertiesDiffs) {
27+
// The routing layer only sends properties registered here, so missing
28+
// settings are an invariant violation.
2729
const auto &settings = settings_.at(propertyName);
28-
auto fromValue = parsePlatformValue(propertyName, propertyDiff.first);
29-
auto toValue = parsePlatformValue(propertyName, propertyDiff.second);
30-
applyEntry(propertyName, fromValue, toValue, settings, timestamp);
30+
applyEntry(propertyName, propertyDiff.first, propertyDiff.second, settings, timestamp);
3131
}
3232
}
3333

@@ -42,17 +42,6 @@ void CSSPlatformTransition::updateSettings(
4242
}
4343
}
4444

45-
void CSSPlatformTransition::runEntry(
46-
jsi::Runtime &rt,
47-
const CSSPlatformTransitionRawEntry &entry,
48-
const double timestamp) {
49-
// null/undefined endpoints are resolved to the property's default inside
50-
// parsePlatformValue; type mismatches and unsupported properties throw.
51-
auto fromValue = parsePlatformValue(rt, entry.propertyName, entry.valueDiff.first);
52-
auto toValue = parsePlatformValue(rt, entry.propertyName, entry.valueDiff.second);
53-
applyEntry(entry.propertyName, fromValue, toValue, entry.settings, timestamp);
54-
}
55-
5645
void CSSPlatformTransition::applyEntry(
5746
const std::string &propertyName,
5847
PlatformValue fromValue,

packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/transition/CSSPlatformTransition.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
#pragma once
22

33
#include <reanimated/CSS/core/transition/CSSPlatformTransitionProxy.h>
4-
#include <reanimated/CSS/utils/platform.h>
54
#include <reanimated/CSS/utils/reversingShortening.h>
65

7-
#include <jsi/jsi.h>
86
#include <react/renderer/core/ReactPrimitives.h>
97

108
#include <memory>
@@ -23,9 +21,8 @@ class CSSPlatformTransition {
2321

2422
CSSPlatformTransition(const CSSPlatformTransition &) = delete;
2523

26-
void run(jsi::Runtime &rt, const CSSPlatformTransitionConfig &config, double timestamp);
27-
/** TODO: unify folly::dynamic and jsi::value versions */
28-
void run(const PropertyValueDynamicDiffsMap &propertiesDiffs, double timestamp);
24+
void run(const CSSPlatformTransitionConfig &config, double timestamp);
25+
void run(const ParsedPlatformDiffs &propertiesDiffs, double timestamp);
2926

3027
void updateSettings(
3128
const PropertiesSettingsMap &changedPropertiesSettings,
@@ -41,7 +38,6 @@ class CSSPlatformTransition {
4138
ReversingState previous;
4239
};
4340

44-
void runEntry(jsi::Runtime &rt, const CSSPlatformTransitionRawEntry &entry, double timestamp);
4541
void applyEntry(
4642
const std::string &propertyName,
4743
PlatformValue fromValue,

packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/transition/CSSPlatformTransitionProxy.cpp

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#include <reanimated/CSS/core/transition/CSSPlatformTransitionProxy.h>
2-
#include <reanimated/Tools/FeatureFlags.h>
32

43
#include <react/debug/react_native_assert.h>
54

@@ -9,19 +8,33 @@ namespace reanimated::css {
98

109
CSSPlatformTransitionProxy::CSSPlatformTransitionProxy(
1110
CSSCanRoutePropertyFunction canRoute,
11+
CSSParseValueFunction parseValue,
12+
CSSParseDynamicValueFunction parseDynamicValue,
1213
CSSApplyTransitionFunction applyTransition,
1314
CSSRemoveTransitionFunction removeTransition)
1415
: canRoute_(std::move(canRoute)),
16+
parseValue_(std::move(parseValue)),
17+
parseDynamicValue_(std::move(parseDynamicValue)),
1518
applyTransition_(std::move(applyTransition)),
1619
removeTransition_(std::move(removeTransition)) {}
1720

1821
bool CSSPlatformTransitionProxy::canRoute(const std::string &propertyName, const EasingConfig &easing) const {
19-
if constexpr (!StaticFeatureFlags::getFlag("IOS_CSS_CORE_ANIMATION")) {
20-
return false;
21-
}
2222
return canRoute_ && canRoute_(propertyName, easing);
2323
}
2424

25+
std::optional<PlatformValue> CSSPlatformTransitionProxy::parseValue(
26+
jsi::Runtime &rt,
27+
const std::string &propertyName,
28+
const jsi::Value &value) const {
29+
return parseValue_ ? parseValue_(rt, propertyName, value) : std::nullopt;
30+
}
31+
32+
std::optional<PlatformValue> CSSPlatformTransitionProxy::parseDynamicValue(
33+
const std::string &propertyName,
34+
const folly::dynamic &value) const {
35+
return parseDynamicValue_ ? parseDynamicValue_(propertyName, value) : std::nullopt;
36+
}
37+
2538
void CSSPlatformTransitionProxy::run(const CSSPlatformTransitionPropertyConfig &config) const {
2639
if (applyTransition_) {
2740
applyTransition_(config);
@@ -34,35 +47,42 @@ void CSSPlatformTransitionProxy::remove(const Tag viewTag, const std::string &pr
3447
}
3548
}
3649

37-
// Splits the new split-shape config into loop / platform buckets. result.routing
38-
// starts as a copy of the previous call's routing and is updated as we route
39-
// each prop; erasing from the *other* side's set returns nonzero exactly when
40-
// the prop is migrating sides - that's how we emit cancels.
50+
// Erasing from the other side's routing set returns nonzero exactly when the
51+
// property migrates sides - that's when the old side gets a cancel.
4152
CSSPlatformTransitionProxy::ProcessedConfig CSSPlatformTransitionProxy::processConfig(
53+
jsi::Runtime &rt,
4254
CSSTransitionConfig &&config,
4355
const CSSTransitionRouting &previousRouting) const {
4456
ProcessedConfig result;
4557
result.routing = previousRouting;
4658

47-
// Drain changedPropertiesSettings; for each, decide routing and bucket.
48-
// extract() preserves move-only PropertyValueDiff (jsi::Value pair).
59+
// extract() preserves the move-only jsi::Value diffs.
4960
while (!config.changedPropertiesSettings.empty()) {
5061
auto settingsNode = config.changedPropertiesSettings.extract(config.changedPropertiesSettings.begin());
5162
const auto &propertyName = settingsNode.key();
5263
const auto &settings = settingsNode.mapped();
5364
const auto valueIt = config.changedProperties.find(propertyName);
5465

55-
if (canRoute(propertyName, settings.easingConfig)) {
66+
bool routable = canRoute(propertyName, settings.easingConfig);
67+
std::optional<PlatformValue> fromValue;
68+
std::optional<PlatformValue> toValue;
69+
if (routable && valueIt != config.changedProperties.end()) {
70+
fromValue = parseValue(rt, propertyName, valueIt->second.first);
71+
toValue = parseValue(rt, propertyName, valueIt->second.second);
72+
routable = fromValue.has_value() && toValue.has_value();
73+
}
74+
75+
if (routable) {
5676
// loop -> platform migration: cancel on loop.
5777
if (result.routing.loop.erase(propertyName) > 0) {
5878
result.loop.removedProperties.push_back(propertyName);
5979
}
6080
result.routing.platform.insert(propertyName);
6181

6282
if (valueIt != config.changedProperties.end()) {
63-
auto valueNode = config.changedProperties.extract(valueIt);
83+
config.changedProperties.erase(valueIt);
6484
result.platform.changedProperties.push_back(
65-
CSSPlatformTransitionRawEntry{propertyName, std::move(valueNode.mapped()), settings});
85+
CSSPlatformTransitionEntry{propertyName, *fromValue, *toValue, settings});
6686
}
6787
result.platform.changedPropertiesSettings.insert(std::move(settingsNode));
6888
} else {
@@ -84,8 +104,7 @@ CSSPlatformTransitionProxy::ProcessedConfig CSSPlatformTransitionProxy::processC
84104
// have consumed all of changedProperties.
85105
react_native_assert(config.changedProperties.empty() && "[Reanimated] CSS transition value diff without settings");
86106

87-
// Props JS asked to stop transitioning: look up the owning side in routing
88-
// and forward the cancel there.
107+
// Forward removal cancels to the side that owns the property.
89108
for (auto &propertyName : config.removedProperties) {
90109
if (result.routing.platform.erase(propertyName) > 0) {
91110
result.platform.removedProperties.push_back(std::move(propertyName));
@@ -97,4 +116,25 @@ CSSPlatformTransitionProxy::ProcessedConfig CSSPlatformTransitionProxy::processC
97116
return result;
98117
}
99118

119+
CSSPlatformTransitionProxy::ProcessedDynamicDiffs CSSPlatformTransitionProxy::processDynamicDiffs(
120+
const CSSTransitionRouting &routing,
121+
const PropertyValueDynamicDiffsMap &propertyDiffs) const {
122+
ProcessedDynamicDiffs result;
123+
for (const auto &[propertyName, propertyDiff] : propertyDiffs) {
124+
// The routing was decided when the transition was registered; here we only
125+
// re-parse the toggled endpoints. Values the platform cannot express fall
126+
// back to the loop.
127+
if (routing.platform.contains(propertyName)) {
128+
auto fromValue = parseDynamicValue(propertyName, propertyDiff.first);
129+
auto toValue = parseDynamicValue(propertyName, propertyDiff.second);
130+
if (fromValue && toValue) {
131+
result.platform.emplace(propertyName, std::make_pair(*fromValue, *toValue));
132+
continue;
133+
}
134+
}
135+
result.loop.emplace(propertyName, propertyDiff);
136+
}
137+
return result;
138+
}
139+
100140
} // namespace reanimated::css

packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/transition/CSSPlatformTransitionProxy.h

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,38 @@
33
#include <reanimated/CSS/common/definitions.h>
44
#include <reanimated/CSS/configs/CSSTransitionConfig.h>
55
#include <reanimated/CSS/easing/EasingConfigs.h>
6-
#include <reanimated/CSS/utils/platform.h>
76

7+
#include <folly/dynamic.h>
88
#include <jsi/jsi.h>
99
#include <react/renderer/core/ReactPrimitives.h>
1010

1111
#include <functional>
12+
#include <optional>
1213
#include <string>
14+
#include <unordered_map>
15+
#include <utility>
1316
#include <vector>
1417

18+
// PlatformValue is an abstract, platform-dependent type: each platform defines
19+
// the value shapes it can animate natively. Shared code only stores, moves and
20+
// compares values produced by the platform's parse hooks.
21+
#ifdef __APPLE__
22+
#include <reanimated/apple/CSS/REACSSPlatformValue.h>
23+
#else
24+
#include <variant>
25+
#endif
26+
1527
namespace reanimated::css {
1628

1729
using namespace facebook;
1830
using namespace react;
1931

20-
// Fully-parsed property config handed to the platform-side apply callback.
32+
#ifndef __APPLE__
33+
using PlatformValue = std::monostate;
34+
#endif
35+
36+
using ParsedPlatformDiffs = std::unordered_map<std::string, std::pair<PlatformValue, PlatformValue>>;
37+
2138
struct CSSPlatformTransitionPropertyConfig {
2239
Tag viewTag;
2340
std::string propertyName;
@@ -28,26 +45,32 @@ struct CSSPlatformTransitionPropertyConfig {
2845
EasingConfig easing;
2946
};
3047

31-
// Raw entry the proxy hands off to CSSPlatformTransition before any value
32-
// parsing happens. Carries the original jsi value pair + timing settings so
33-
// the platform side can convert lazily and inspect both from/to if needed.
34-
struct CSSPlatformTransitionRawEntry {
48+
struct CSSPlatformTransitionEntry {
3549
std::string propertyName;
36-
PropertyValueDiff valueDiff;
50+
PlatformValue fromValue;
51+
PlatformValue toValue;
3752
CSSTransitionPropertySettings settings;
3853
};
3954

4055
struct CSSPlatformTransitionConfig {
4156
PropertiesSettingsMap changedPropertiesSettings;
42-
std::vector<CSSPlatformTransitionRawEntry> changedProperties;
57+
std::vector<CSSPlatformTransitionEntry> changedProperties;
4358
std::vector<std::string> removedProperties;
4459

4560
bool empty() const {
4661
return changedPropertiesSettings.empty() && changedProperties.empty() && removedProperties.empty();
4762
}
4863
};
4964

65+
// Whether the platform can animate the property natively for the given easing.
5066
using CSSCanRoutePropertyFunction = std::function<bool(const std::string &propertyName, const EasingConfig &easing)>;
67+
// Converts a transition endpoint into the platform's value representation; null
68+
// endpoints resolve to the property's CSS default. nullopt means the value is
69+
// not expressible natively and the property runs on the loop.
70+
using CSSParseValueFunction = std::function<
71+
std::optional<PlatformValue>(jsi::Runtime &rt, const std::string &propertyName, const jsi::Value &value)>;
72+
using CSSParseDynamicValueFunction =
73+
std::function<std::optional<PlatformValue>(const std::string &propertyName, const folly::dynamic &value)>;
5174
using CSSApplyTransitionFunction = std::function<void(const CSSPlatformTransitionPropertyConfig &config)>;
5275
using CSSRemoveTransitionFunction = std::function<void(Tag viewTag, const std::string &propertyName)>;
5376

@@ -64,8 +87,15 @@ class CSSPlatformTransitionProxy {
6487
CSSTransitionRouting routing;
6588
};
6689

90+
struct ProcessedDynamicDiffs {
91+
PropertyValueDynamicDiffsMap loop;
92+
ParsedPlatformDiffs platform;
93+
};
94+
6795
CSSPlatformTransitionProxy(
6896
CSSCanRoutePropertyFunction canRoute,
97+
CSSParseValueFunction parseValue,
98+
CSSParseDynamicValueFunction parseDynamicValue,
6999
CSSApplyTransitionFunction applyTransition,
70100
CSSRemoveTransitionFunction removeTransition);
71101

@@ -74,14 +104,27 @@ class CSSPlatformTransitionProxy {
74104

75105
// Filters the incoming config into loop / platform buckets and emits implicit
76106
// cancels on the old side when a property migrates compared to previousRouting.
77-
// The platform side does its own value parsing inside CSSPlatformTransition::run -
78-
// the proxy only forwards the raw jsi value pair via the raw entry.
79-
ProcessedConfig processConfig(CSSTransitionConfig &&config, const CSSTransitionRouting &previousRouting) const;
107+
// A property routes to the platform only when the platform declares it routable
108+
// and both endpoint values parse to platform values.
109+
ProcessedConfig
110+
processConfig(jsi::Runtime &rt, CSSTransitionConfig &&config, const CSSTransitionRouting &previousRouting) const;
111+
112+
// Splits already-routed pseudo-selector toggle diffs into loop / platform
113+
// buckets, parsing the platform endpoints; values the platform cannot express
114+
// fall back to the loop.
115+
ProcessedDynamicDiffs processDynamicDiffs(
116+
const CSSTransitionRouting &routing,
117+
const PropertyValueDynamicDiffsMap &propertyDiffs) const;
80118

81119
private:
82120
bool canRoute(const std::string &propertyName, const EasingConfig &easing) const;
121+
std::optional<PlatformValue> parseValue(jsi::Runtime &rt, const std::string &propertyName, const jsi::Value &value)
122+
const;
123+
std::optional<PlatformValue> parseDynamicValue(const std::string &propertyName, const folly::dynamic &value) const;
83124

84125
CSSCanRoutePropertyFunction canRoute_;
126+
CSSParseValueFunction parseValue_;
127+
CSSParseDynamicValueFunction parseDynamicValue_;
85128
CSSApplyTransitionFunction applyTransition_;
86129
CSSRemoveTransitionFunction removeTransition_;
87130
};

0 commit comments

Comments
 (0)