Skip to content

Commit e043d70

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

21 files changed

Lines changed: 345 additions & 195 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 & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
#include <reanimated/CSS/utils/platform.h>
55
#include <reanimated/CSS/utils/reversingShortening.h>
66

7-
#include <jsi/jsi.h>
87
#include <react/renderer/core/ReactPrimitives.h>
98

109
#include <memory>
@@ -23,9 +22,8 @@ class CSSPlatformTransition {
2322

2423
CSSPlatformTransition(const CSSPlatformTransition &) = delete;
2524

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);
25+
void run(const CSSPlatformTransitionConfig &config, double timestamp);
26+
void run(const ParsedPlatformDiffs &propertiesDiffs, double timestamp);
2927

3028
void updateSettings(
3129
const PropertiesSettingsMap &changedPropertiesSettings,
@@ -41,7 +39,6 @@ class CSSPlatformTransition {
4139
ReversingState previous;
4240
};
4341

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

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

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#include <reanimated/CSS/core/transition/CSSPlatformTransitionProxy.h>
2-
#include <reanimated/Tools/FeatureFlags.h>
2+
#include <reanimated/CSS/utils/CSSTransitionRouting.h>
33

44
#include <react/debug/react_native_assert.h>
55

@@ -8,19 +8,9 @@
88
namespace reanimated::css {
99

1010
CSSPlatformTransitionProxy::CSSPlatformTransitionProxy(
11-
CSSCanRoutePropertyFunction canRoute,
1211
CSSApplyTransitionFunction applyTransition,
1312
CSSRemoveTransitionFunction removeTransition)
14-
: canRoute_(std::move(canRoute)),
15-
applyTransition_(std::move(applyTransition)),
16-
removeTransition_(std::move(removeTransition)) {}
17-
18-
bool CSSPlatformTransitionProxy::canRoute(const std::string &propertyName, const EasingConfig &easing) const {
19-
if constexpr (!StaticFeatureFlags::getFlag("IOS_CSS_CORE_ANIMATION")) {
20-
return false;
21-
}
22-
return canRoute_ && canRoute_(propertyName, easing);
23-
}
13+
: applyTransition_(std::move(applyTransition)), removeTransition_(std::move(removeTransition)) {}
2414

2515
void CSSPlatformTransitionProxy::run(const CSSPlatformTransitionPropertyConfig &config) const {
2616
if (applyTransition_) {
@@ -34,35 +24,42 @@ void CSSPlatformTransitionProxy::remove(const Tag viewTag, const std::string &pr
3424
}
3525
}
3626

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.
27+
// Erasing from the other side's routing set returns nonzero exactly when the
28+
// property migrates sides - that's when the old side gets a cancel.
4129
CSSPlatformTransitionProxy::ProcessedConfig CSSPlatformTransitionProxy::processConfig(
30+
jsi::Runtime &rt,
4231
CSSTransitionConfig &&config,
4332
const CSSTransitionRouting &previousRouting) const {
4433
ProcessedConfig result;
4534
result.routing = previousRouting;
4635

47-
// Drain changedPropertiesSettings; for each, decide routing and bucket.
48-
// extract() preserves move-only PropertyValueDiff (jsi::Value pair).
36+
// extract() preserves the move-only jsi::Value diffs.
4937
while (!config.changedPropertiesSettings.empty()) {
5038
auto settingsNode = config.changedPropertiesSettings.extract(config.changedPropertiesSettings.begin());
5139
const auto &propertyName = settingsNode.key();
5240
const auto &settings = settingsNode.mapped();
5341
const auto valueIt = config.changedProperties.find(propertyName);
5442

55-
if (canRoute(propertyName, settings.easingConfig)) {
43+
bool routable = canRouteCSSProperty(propertyName, settings.easingConfig);
44+
std::optional<PlatformValue> fromValue;
45+
std::optional<PlatformValue> toValue;
46+
if (routable && valueIt != config.changedProperties.end()) {
47+
fromValue = parsePlatformValue(rt, propertyName, valueIt->second.first);
48+
toValue = parsePlatformValue(rt, propertyName, valueIt->second.second);
49+
routable = fromValue.has_value() && toValue.has_value();
50+
}
51+
52+
if (routable) {
5653
// loop -> platform migration: cancel on loop.
5754
if (result.routing.loop.erase(propertyName) > 0) {
5855
result.loop.removedProperties.push_back(propertyName);
5956
}
6057
result.routing.platform.insert(propertyName);
6158

6259
if (valueIt != config.changedProperties.end()) {
63-
auto valueNode = config.changedProperties.extract(valueIt);
60+
config.changedProperties.erase(valueIt);
6461
result.platform.changedProperties.push_back(
65-
CSSPlatformTransitionRawEntry{propertyName, std::move(valueNode.mapped()), settings});
62+
CSSPlatformTransitionEntry{propertyName, *fromValue, *toValue, settings});
6663
}
6764
result.platform.changedPropertiesSettings.insert(std::move(settingsNode));
6865
} else {
@@ -84,8 +81,7 @@ CSSPlatformTransitionProxy::ProcessedConfig CSSPlatformTransitionProxy::processC
8481
// have consumed all of changedProperties.
8582
react_native_assert(config.changedProperties.empty() && "[Reanimated] CSS transition value diff without settings");
8683

87-
// Props JS asked to stop transitioning: look up the owning side in routing
88-
// and forward the cancel there.
84+
// Forward removal cancels to the side that owns the property.
8985
for (auto &propertyName : config.removedProperties) {
9086
if (result.routing.platform.erase(propertyName) > 0) {
9187
result.platform.removedProperties.push_back(std::move(propertyName));

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

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ namespace reanimated::css {
1717
using namespace facebook;
1818
using namespace react;
1919

20-
// Fully-parsed property config handed to the platform-side apply callback.
2120
struct CSSPlatformTransitionPropertyConfig {
2221
Tag viewTag;
2322
std::string propertyName;
@@ -28,26 +27,23 @@ struct CSSPlatformTransitionPropertyConfig {
2827
EasingConfig easing;
2928
};
3029

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 {
30+
struct CSSPlatformTransitionEntry {
3531
std::string propertyName;
36-
PropertyValueDiff valueDiff;
32+
PlatformValue fromValue;
33+
PlatformValue toValue;
3734
CSSTransitionPropertySettings settings;
3835
};
3936

4037
struct CSSPlatformTransitionConfig {
4138
PropertiesSettingsMap changedPropertiesSettings;
42-
std::vector<CSSPlatformTransitionRawEntry> changedProperties;
39+
std::vector<CSSPlatformTransitionEntry> changedProperties;
4340
std::vector<std::string> removedProperties;
4441

4542
bool empty() const {
4643
return changedPropertiesSettings.empty() && changedProperties.empty() && removedProperties.empty();
4744
}
4845
};
4946

50-
using CSSCanRoutePropertyFunction = std::function<bool(const std::string &propertyName, const EasingConfig &easing)>;
5147
using CSSApplyTransitionFunction = std::function<void(const CSSPlatformTransitionPropertyConfig &config)>;
5248
using CSSRemoveTransitionFunction = std::function<void(Tag viewTag, const std::string &propertyName)>;
5349

@@ -64,24 +60,19 @@ class CSSPlatformTransitionProxy {
6460
CSSTransitionRouting routing;
6561
};
6662

67-
CSSPlatformTransitionProxy(
68-
CSSCanRoutePropertyFunction canRoute,
69-
CSSApplyTransitionFunction applyTransition,
70-
CSSRemoveTransitionFunction removeTransition);
63+
CSSPlatformTransitionProxy(CSSApplyTransitionFunction applyTransition, CSSRemoveTransitionFunction removeTransition);
7164

7265
void run(const CSSPlatformTransitionPropertyConfig &config) const;
7366
void remove(Tag viewTag, const std::string &propertyName) const;
7467

7568
// Filters the incoming config into loop / platform buckets and emits implicit
7669
// 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;
70+
// A property routes to the platform only when the platform declares it routable
71+
// and both endpoint values parse to platform values.
72+
ProcessedConfig
73+
processConfig(jsi::Runtime &rt, CSSTransitionConfig &&config, const CSSTransitionRouting &previousRouting) const;
8074

8175
private:
82-
bool canRoute(const std::string &propertyName, const EasingConfig &easing) const;
83-
84-
CSSCanRoutePropertyFunction canRoute_;
8576
CSSApplyTransitionFunction applyTransition_;
8677
CSSRemoveTransitionFunction removeTransition_;
8778
};

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

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,14 @@ TransitionProperties CSSTransition::getProperties() const {
3636
folly::dynamic CSSTransition::run(jsi::Runtime &rt, CSSTransitionConfig &&config, const folly::dynamic &lastUpdates) {
3737
const auto timestamp = loop_->resolveTimestamp();
3838

39-
// CSSTransition owns routing: platform-routed props run immediately on the platform
40-
// transition; the loop-routed remainder is applied to the loop transition below.
41-
auto processed = platformTransitionProxy_->processConfig(std::move(config), routing_);
39+
auto processed = platformTransitionProxy_->processConfig(rt, std::move(config), routing_);
4240
routing_ = std::move(processed.routing);
4341

4442
if (!processed.platform.empty()) {
4543
auto &platformTransition = ensurePlatformTransition();
4644
platformTransition.updateSettings(
4745
processed.platform.changedPropertiesSettings, processed.platform.removedProperties);
48-
platformTransition.run(rt, processed.platform, timestamp);
46+
platformTransition.run(processed.platform, timestamp);
4947
}
5048

5149
const auto &loopConfig = processed.loop;
@@ -72,13 +70,18 @@ folly::dynamic CSSTransition::run(
7270
const auto timestamp = loop_->resolveTimestamp();
7371

7472
PropertyValueDynamicDiffsMap loopDiffs;
75-
PropertyValueDynamicDiffsMap platformDiffs;
73+
ParsedPlatformDiffs platformDiffs;
7674
for (const auto &[propertyName, propertyDiff] : propertyDiffs) {
75+
// Values the platform cannot express fall back to the loop.
7776
if (routing_.platform.contains(propertyName)) {
78-
platformDiffs.emplace(propertyName, propertyDiff);
79-
} else {
80-
loopDiffs.emplace(propertyName, propertyDiff);
77+
auto fromValue = parsePlatformValue(propertyName, propertyDiff.first);
78+
auto toValue = parsePlatformValue(propertyName, propertyDiff.second);
79+
if (fromValue && toValue) {
80+
platformDiffs.emplace(propertyName, std::make_pair(*fromValue, *toValue));
81+
continue;
82+
}
8183
}
84+
loopDiffs.emplace(propertyName, propertyDiff);
8285
}
8386

8487
if (!platformDiffs.empty()) {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#include <reanimated/CSS/utils/CSSTransitionRouting.h>
2+
3+
#ifndef __APPLE__
4+
5+
// Platforms without native transition support route everything to the loop.
6+
namespace reanimated::css {
7+
8+
bool canRouteCSSProperty(const std::string & /*propertyName*/, const EasingConfig & /*easing*/) {
9+
return false;
10+
}
11+
12+
} // namespace reanimated::css
13+
14+
#endif
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#pragma once
2+
3+
#include <reanimated/CSS/easing/EasingConfigs.h>
4+
5+
#include <string>
6+
7+
namespace reanimated::css {
8+
9+
// Routing predicate for platform-driven CSS transitions: whether the platform
10+
// can animate the property natively for the given easing. Declared here and
11+
// implemented per platform (iOS in REACSSTransitionRouting.mm; the non-platform
12+
// stub in CSSTransitionRouting.cpp routes everything to the loop).
13+
bool canRouteCSSProperty(const std::string &propertyName, const EasingConfig &easing);
14+
15+
} // namespace reanimated::css
Lines changed: 11 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,21 @@
11
#include <reanimated/CSS/utils/platform.h>
22

3-
#include <unordered_map>
3+
#ifndef __APPLE__
44

5+
// Platforms without native transition support route everything to the loop.
56
namespace reanimated::css {
67

7-
namespace {
8-
9-
// CSS-spec defaults; mirror Common/cpp/reanimated/CSS/InterpolatorRegistry.cpp.
10-
const std::unordered_map<std::string, PlatformValue> kDefaults = {
11-
{"opacity", 1.0},
12-
};
13-
14-
PlatformValue parseDouble(const jsi::Value &value) {
15-
return value.isNumber() ? PlatformValue(value.asNumber()) : PlatformValue{};
16-
}
17-
18-
} // namespace
19-
20-
PlatformValue parsePlatformValue(jsi::Runtime &rt, const std::string &propertyName, const jsi::Value &value) {
21-
if (value.isNull() || value.isUndefined()) {
22-
const auto it = kDefaults.find(propertyName);
23-
return it != kDefaults.end() ? it->second : PlatformValue{};
24-
}
25-
if (propertyName == "opacity") {
26-
return parseDouble(value);
27-
}
28-
return PlatformValue{};
8+
std::optional<PlatformValue>
9+
parsePlatformValue(jsi::Runtime & /*rt*/, const std::string & /*propertyName*/, const jsi::Value & /*value*/) {
10+
return std::nullopt;
2911
}
3012

31-
PlatformValue parsePlatformValue(const std::string &propertyName, const folly::dynamic &value) {
32-
if (value.isNull()) {
33-
const auto it = kDefaults.find(propertyName);
34-
return it != kDefaults.end() ? it->second : PlatformValue{};
35-
}
36-
if (propertyName == "opacity") {
37-
return value.isNumber() ? PlatformValue(value.asDouble()) : PlatformValue{};
38-
}
39-
return PlatformValue{};
13+
std::optional<PlatformValue> parsePlatformValue(
14+
const std::string & /*propertyName*/,
15+
const folly::dynamic & /*value*/) {
16+
return std::nullopt;
4017
}
4118

4219
} // namespace reanimated::css
20+
21+
#endif

0 commit comments

Comments
 (0)