Skip to content

Commit 3c385cc

Browse files
committed
Route shadow and color transitions to Core Animation
1 parent 47a4295 commit 3c385cc

1 file changed

Lines changed: 84 additions & 4 deletions

File tree

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

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,103 @@
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_view>
512
#import <variant>
613

14+
using namespace facebook::react;
715
using namespace reanimated::css;
816

17+
// Properties expressible as a CABasicAnimation on the view's main layer.
18+
static const std::unordered_set<std::string> &routableProperties()
19+
{
20+
static const std::unordered_set<std::string> kRoutable = {
21+
"opacity",
22+
"shadowOpacity",
23+
"shadowRadius",
24+
"shadowOffset",
25+
"shadowColor",
26+
"backgroundColor",
27+
"borderColor",
28+
};
29+
return kRoutable;
30+
}
31+
32+
// Per-side colors that subvert the uniform `borderColor` when they transition
33+
// together with it - RN then draws the border through its custom path, where
34+
// main-layer animations are invisible.
35+
static 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 the `useCoreAnimationBorderRendering` condition in RN's
57+
// RCTViewComponentView. When it fails, RN zeroes the layer's border properties
58+
// and draws the background + border into dedicated sublayers that get rebuilt
59+
// (and stripped of animations) on every commit - so main-layer keypath
60+
// animations of background/border properties would be invisible there.
61+
static bool usesCoreAnimationBorderRendering(const ShadowNode &shadowNode)
62+
{
63+
const auto *layoutableShadowNode = dynamic_cast<const LayoutableShadowNode *>(&shadowNode);
64+
if (layoutableShadowNode == nullptr) {
65+
return false;
66+
}
67+
const auto viewProps = std::static_pointer_cast<const ViewProps>(shadowNode.getProps());
68+
const auto borderMetrics = viewProps->resolveBorderMetrics(layoutableShadowNode->layoutMetrics_);
69+
return borderMetrics.borderColors.isUniform() && borderMetrics.borderWidths.isUniform() &&
70+
borderMetrics.borderStyles.isUniform() && borderMetrics.borderStyles.left == BorderStyle::Solid &&
71+
borderMetrics.borderRadii.isUniform() &&
72+
(borderMetrics.borderWidths.left == 0 || viewProps->getClipsContentToBounds() ||
73+
(colorComponentsFromColor(borderMetrics.borderColors.left).alpha == 0 &&
74+
(*borderMetrics.borderColors.left).getUIColor() != nullptr));
75+
}
76+
977
bool canRouteCSSProperty(
1078
const std::string &propertyName,
1179
const EasingConfig &easing,
12-
const facebook::react::ShadowNode & /*shadowNode*/,
13-
const std::unordered_set<std::string> & /*transitioningProperties*/)
80+
const facebook::react::ShadowNode &shadowNode,
81+
const std::unordered_set<std::string> &transitioningProperties)
1482
{
1583
if constexpr (!reanimated::StaticFeatureFlags::getFlag("IOS_CSS_CORE_ANIMATION")) {
1684
return false;
1785
}
18-
if (propertyName != "opacity") {
86+
if (!routableProperties().contains(propertyName)) {
1987
return false;
2088
}
2189
// CAMediaTimingFunction can express only linear and cubic-bezier curves;
2290
// steps / linear-stops easings have to interpolate per-frame on the loop.
23-
return std::holds_alternative<LinearEasing>(easing) || std::holds_alternative<CubicBezierEasing>(easing);
91+
if (!std::holds_alternative<LinearEasing>(easing) && !std::holds_alternative<CubicBezierEasing>(easing)) {
92+
return false;
93+
}
94+
// Background and border colors live on the main layer only while RN uses
95+
// Core Animation border rendering for this view.
96+
if ((propertyName == "backgroundColor" || propertyName == "borderColor") &&
97+
!usesCoreAnimationBorderRendering(shadowNode)) {
98+
return false;
99+
}
100+
if (propertyName == "borderColor" && hasPerSideBorderColorSibling(transitioningProperties)) {
101+
return false;
102+
}
103+
return true;
24104
}

0 commit comments

Comments
 (0)