Skip to content

Commit 5a1dcdf

Browse files
committed
Route shadow and color transitions to Core Animation
1 parent 7964685 commit 5a1dcdf

1 file changed

Lines changed: 85 additions & 4 deletions

File tree

packages/react-native-reanimated/apple/reanimated/apple/CSS/REACSSTransitionRouting.mm

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,106 @@
22

33
#import <reanimated/Tools/FeatureFlags.h>
44

5+
#import <react/renderer/components/view/ViewProps.h>
6+
#import <react/renderer/core/LayoutableShadowNode.h>
7+
#import <react/renderer/graphics/Color.h>
8+
9+
#import <array>
10+
#import <memory>
11+
#import <string>
12+
#import <string_view>
513
#import <variant>
614

715
namespace reanimated::css {
816

17+
using namespace facebook::react;
18+
19+
namespace {
20+
21+
// Properties expressible as a CABasicAnimation on the view's main layer.
22+
const std::unordered_set<std::string> kRoutableProperties = {
23+
"opacity",
24+
"shadowOpacity",
25+
"shadowRadius",
26+
"shadowOffset",
27+
"shadowColor",
28+
"backgroundColor",
29+
"borderColor",
30+
};
31+
32+
// Per-side colors subvert the uniform borderColor when they transition with
33+
// it - RN then draws the border through its custom path, where main-layer
34+
// animations are invisible.
35+
bool hasPerSideBorderColorSibling(const std::unordered_set<std::string> &transitioningProperties)
36+
{
37+
static constexpr std::array<std::string_view, 9> kSiblings = {
38+
"borderTopColor",
39+
"borderRightColor",
40+
"borderBottomColor",
41+
"borderLeftColor",
42+
"borderStartColor",
43+
"borderEndColor",
44+
"borderBlockColor",
45+
"borderBlockStartColor",
46+
"borderBlockEndColor",
47+
};
48+
for (const auto &sibling : kSiblings) {
49+
if (transitioningProperties.contains(std::string(sibling))) {
50+
return true;
51+
}
52+
}
53+
return false;
54+
}
55+
56+
// Mirrors useCoreAnimationBorderRendering in RN's RCTViewComponentView: when
57+
// it is false, RN draws the background and border into dedicated sublayers
58+
// that are rebuilt (and stripped of animations) on every commit, so main-layer
59+
// animations of those properties would be invisible.
60+
bool usesCoreAnimationBorderRendering(const ShadowNode &shadowNode)
61+
{
62+
const auto *layoutableShadowNode = dynamic_cast<const LayoutableShadowNode *>(&shadowNode);
63+
if (layoutableShadowNode == nullptr) {
64+
return false;
65+
}
66+
const auto viewProps = std::static_pointer_cast<const ViewProps>(shadowNode.getProps());
67+
const auto borderMetrics = viewProps->resolveBorderMetrics(layoutableShadowNode->getLayoutMetrics());
68+
return borderMetrics.borderColors.isUniform() && borderMetrics.borderWidths.isUniform() &&
69+
borderMetrics.borderStyles.isUniform() && borderMetrics.borderStyles.left == BorderStyle::Solid &&
70+
borderMetrics.borderRadii.isUniform() &&
71+
(borderMetrics.borderWidths.left == 0 || viewProps->getClipsContentToBounds() ||
72+
(colorComponentsFromColor(borderMetrics.borderColors.left).alpha == 0 &&
73+
(*borderMetrics.borderColors.left).getUIColor() != nullptr));
74+
}
75+
76+
} // namespace
77+
978
bool canRouteCSSProperty(
1079
const std::string &propertyName,
1180
const EasingConfig &easing,
12-
const facebook::react::ShadowNode & /*shadowNode*/,
13-
const std::unordered_set<std::string> & /*transitioningProperties*/)
81+
const ShadowNode &shadowNode,
82+
const std::unordered_set<std::string> &transitioningProperties)
1483
{
1584
if constexpr (!StaticFeatureFlags::getFlag("IOS_CSS_CORE_ANIMATION")) {
1685
return false;
1786
}
18-
if (propertyName != "opacity") {
87+
if (!kRoutableProperties.contains(propertyName)) {
1988
return false;
2089
}
2190
// CAMediaTimingFunction can express only linear and cubic-bezier curves;
2291
// steps / linear-stops easings have to interpolate per-frame on the loop.
23-
return std::holds_alternative<LinearEasing>(easing) || std::holds_alternative<CubicBezierEasing>(easing);
92+
if (!std::holds_alternative<LinearEasing>(easing) && !std::holds_alternative<CubicBezierEasing>(easing)) {
93+
return false;
94+
}
95+
// Background and border colors live on the main layer only while RN uses
96+
// Core Animation border rendering for this view.
97+
if ((propertyName == "backgroundColor" || propertyName == "borderColor") &&
98+
!usesCoreAnimationBorderRendering(shadowNode)) {
99+
return false;
100+
}
101+
if (propertyName == "borderColor" && hasPerSideBorderColorSibling(transitioningProperties)) {
102+
return false;
103+
}
104+
return true;
24105
}
25106

26107
} // namespace reanimated::css

0 commit comments

Comments
 (0)