Skip to content

Commit fafdab0

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

18 files changed

Lines changed: 378 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: 88 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,73 @@
11
#include <reanimated/CSS/core/transition/CSSPlatformTransitionProxy.h>
2-
#include <reanimated/Tools/FeatureFlags.h>
32

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

65
#include <utility>
76

87
namespace reanimated::css {
98

9+
namespace {
10+
11+
// Reduces a jsi::Value transition endpoint to the representation-agnostic shape
12+
// the platform parses (number / size / null).
13+
CSSPropertyValue endpointFromJSI(jsi::Runtime &rt, const jsi::Value &value) {
14+
CSSPropertyValue result;
15+
if (value.isNull() || value.isUndefined()) {
16+
result.isNull = true;
17+
} else if (value.isNumber()) {
18+
result.number = value.asNumber();
19+
} else if (value.isObject()) {
20+
const auto object = value.asObject(rt);
21+
const auto width = object.getProperty(rt, "width");
22+
const auto height = object.getProperty(rt, "height");
23+
result.size = std::array<double, 2>{
24+
width.isNumber() ? width.asNumber() : 0.0,
25+
height.isNumber() ? height.asNumber() : 0.0,
26+
};
27+
}
28+
return result;
29+
}
30+
31+
// The same reduction for the runtime-free pseudo-selector toggle path.
32+
CSSPropertyValue endpointFromDynamic(const folly::dynamic &value) {
33+
CSSPropertyValue result;
34+
if (value.isNull()) {
35+
result.isNull = true;
36+
} else if (value.isNumber()) {
37+
result.number = value.asDouble();
38+
} else if (value.isObject()) {
39+
const auto *width = value.get_ptr("width");
40+
const auto *height = value.get_ptr("height");
41+
result.size = std::array<double, 2>{
42+
width != nullptr && width->isNumber() ? width->asDouble() : 0.0,
43+
height != nullptr && height->isNumber() ? height->asDouble() : 0.0,
44+
};
45+
}
46+
return result;
47+
}
48+
49+
} // namespace
50+
1051
CSSPlatformTransitionProxy::CSSPlatformTransitionProxy(
1152
CSSCanRoutePropertyFunction canRoute,
53+
CSSParseValueFunction parseValue,
1254
CSSApplyTransitionFunction applyTransition,
1355
CSSRemoveTransitionFunction removeTransition)
1456
: canRoute_(std::move(canRoute)),
57+
parseValue_(std::move(parseValue)),
1558
applyTransition_(std::move(applyTransition)),
1659
removeTransition_(std::move(removeTransition)) {}
1760

1861
bool CSSPlatformTransitionProxy::canRoute(const std::string &propertyName, const EasingConfig &easing) const {
19-
if constexpr (!StaticFeatureFlags::getFlag("IOS_CSS_CORE_ANIMATION")) {
20-
return false;
21-
}
2262
return canRoute_ && canRoute_(propertyName, easing);
2363
}
2464

65+
std::optional<PlatformValue> CSSPlatformTransitionProxy::parseValue(
66+
const std::string &propertyName,
67+
const CSSPropertyValue &value) const {
68+
return parseValue_ ? parseValue_(propertyName, value) : std::nullopt;
69+
}
70+
2571
void CSSPlatformTransitionProxy::run(const CSSPlatformTransitionPropertyConfig &config) const {
2672
if (applyTransition_) {
2773
applyTransition_(config);
@@ -34,35 +80,42 @@ void CSSPlatformTransitionProxy::remove(const Tag viewTag, const std::string &pr
3480
}
3581
}
3682

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.
83+
// Erasing from the other side's routing set returns nonzero exactly when the
84+
// property migrates sides - that's when the old side gets a cancel.
4185
CSSPlatformTransitionProxy::ProcessedConfig CSSPlatformTransitionProxy::processConfig(
86+
jsi::Runtime &rt,
4287
CSSTransitionConfig &&config,
4388
const CSSTransitionRouting &previousRouting) const {
4489
ProcessedConfig result;
4590
result.routing = previousRouting;
4691

47-
// Drain changedPropertiesSettings; for each, decide routing and bucket.
48-
// extract() preserves move-only PropertyValueDiff (jsi::Value pair).
92+
// extract() preserves the move-only jsi::Value diffs.
4993
while (!config.changedPropertiesSettings.empty()) {
5094
auto settingsNode = config.changedPropertiesSettings.extract(config.changedPropertiesSettings.begin());
5195
const auto &propertyName = settingsNode.key();
5296
const auto &settings = settingsNode.mapped();
5397
const auto valueIt = config.changedProperties.find(propertyName);
5498

55-
if (canRoute(propertyName, settings.easingConfig)) {
99+
bool routable = canRoute(propertyName, settings.easingConfig);
100+
std::optional<PlatformValue> fromValue;
101+
std::optional<PlatformValue> toValue;
102+
if (routable && valueIt != config.changedProperties.end()) {
103+
fromValue = parseValue(propertyName, endpointFromJSI(rt, valueIt->second.first));
104+
toValue = parseValue(propertyName, endpointFromJSI(rt, valueIt->second.second));
105+
routable = fromValue.has_value() && toValue.has_value();
106+
}
107+
108+
if (routable) {
56109
// loop -> platform migration: cancel on loop.
57110
if (result.routing.loop.erase(propertyName) > 0) {
58111
result.loop.removedProperties.push_back(propertyName);
59112
}
60113
result.routing.platform.insert(propertyName);
61114

62115
if (valueIt != config.changedProperties.end()) {
63-
auto valueNode = config.changedProperties.extract(valueIt);
116+
config.changedProperties.erase(valueIt);
64117
result.platform.changedProperties.push_back(
65-
CSSPlatformTransitionRawEntry{propertyName, std::move(valueNode.mapped()), settings});
118+
CSSPlatformTransitionEntry{propertyName, *fromValue, *toValue, settings});
66119
}
67120
result.platform.changedPropertiesSettings.insert(std::move(settingsNode));
68121
} else {
@@ -84,8 +137,7 @@ CSSPlatformTransitionProxy::ProcessedConfig CSSPlatformTransitionProxy::processC
84137
// have consumed all of changedProperties.
85138
react_native_assert(config.changedProperties.empty() && "[Reanimated] CSS transition value diff without settings");
86139

87-
// Props JS asked to stop transitioning: look up the owning side in routing
88-
// and forward the cancel there.
140+
// Forward removal cancels to the side that owns the property.
89141
for (auto &propertyName : config.removedProperties) {
90142
if (result.routing.platform.erase(propertyName) > 0) {
91143
result.platform.removedProperties.push_back(std::move(propertyName));
@@ -97,4 +149,25 @@ CSSPlatformTransitionProxy::ProcessedConfig CSSPlatformTransitionProxy::processC
97149
return result;
98150
}
99151

152+
CSSPlatformTransitionProxy::ProcessedDynamicDiffs CSSPlatformTransitionProxy::processDynamicDiffs(
153+
const CSSTransitionRouting &routing,
154+
const PropertyValueDynamicDiffsMap &propertyDiffs) const {
155+
ProcessedDynamicDiffs result;
156+
for (const auto &[propertyName, propertyDiff] : propertyDiffs) {
157+
// The routing was decided when the transition was registered; here we only
158+
// re-parse the toggled endpoints. Values the platform cannot express fall
159+
// back to the loop.
160+
if (routing.platform.contains(propertyName)) {
161+
auto fromValue = parseValue(propertyName, endpointFromDynamic(propertyDiff.first));
162+
auto toValue = parseValue(propertyName, endpointFromDynamic(propertyDiff.second));
163+
if (fromValue && toValue) {
164+
result.platform.emplace(propertyName, std::make_pair(*fromValue, *toValue));
165+
continue;
166+
}
167+
}
168+
result.loop.emplace(propertyName, propertyDiff);
169+
}
170+
return result;
171+
}
172+
100173
} // namespace reanimated::css

0 commit comments

Comments
 (0)