Skip to content

Commit 830492b

Browse files
committed
Route shadow and color transitions to Core Animation
1 parent e445fea commit 830492b

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,25 +2,105 @@
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+
// Per-side colors subvert the uniform borderColor when they transition with
22+
// it - RN then draws the border through its custom path, where main-layer
23+
// animations are invisible.
24+
bool hasPerSideBorderColorSibling(const std::unordered_set<std::string> &transitioningProperties)
25+
{
26+
static constexpr std::array<std::string_view, 9> kSiblings = {
27+
"borderTopColor",
28+
"borderRightColor",
29+
"borderBottomColor",
30+
"borderLeftColor",
31+
"borderStartColor",
32+
"borderEndColor",
33+
"borderBlockColor",
34+
"borderBlockStartColor",
35+
"borderBlockEndColor",
36+
};
37+
for (const auto &sibling : kSiblings) {
38+
if (transitioningProperties.contains(std::string(sibling))) {
39+
return true;
40+
}
41+
}
42+
return false;
43+
}
44+
45+
// Mirrors useCoreAnimationBorderRendering in RN's RCTViewComponentView: when
46+
// it is false, RN draws the background and border into dedicated sublayers
47+
// that are rebuilt (and stripped of animations) on every commit, so main-layer
48+
// animations of those properties would be invisible.
49+
bool usesCoreAnimationBorderRendering(const ShadowNode &shadowNode)
50+
{
51+
const auto *layoutableShadowNode = dynamic_cast<const LayoutableShadowNode *>(&shadowNode);
52+
if (layoutableShadowNode == nullptr) {
53+
return false;
54+
}
55+
const auto viewProps = std::static_pointer_cast<const ViewProps>(shadowNode.getProps());
56+
const auto borderMetrics = viewProps->resolveBorderMetrics(layoutableShadowNode->getLayoutMetrics());
57+
return borderMetrics.borderColors.isUniform() && borderMetrics.borderWidths.isUniform() &&
58+
borderMetrics.borderStyles.isUniform() && borderMetrics.borderStyles.left == BorderStyle::Solid &&
59+
borderMetrics.borderRadii.isUniform() &&
60+
(borderMetrics.borderWidths.left == 0 || viewProps->getClipsContentToBounds() ||
61+
(colorComponentsFromColor(borderMetrics.borderColors.left).alpha == 0 &&
62+
(*borderMetrics.borderColors.left).getUIColor() != nullptr));
63+
}
64+
65+
} // namespace
66+
967
bool canRouteCSSProperty(
1068
const std::string &propertyName,
1169
const EasingConfig &easing,
12-
const facebook::react::ShadowNode & /*shadowNode*/,
13-
const std::unordered_set<std::string> & /*transitioningProperties*/)
70+
const ShadowNode &shadowNode,
71+
const std::unordered_set<std::string> &transitioningProperties)
1472
{
1573
if constexpr (!StaticFeatureFlags::getFlag("IOS_CSS_CORE_ANIMATION")) {
1674
return false;
1775
}
18-
if (propertyName != "opacity") {
76+
// Properties expressible as a CABasicAnimation on the view's main layer.
77+
static const std::unordered_set<std::string> kRoutableProperties = {
78+
"opacity",
79+
"shadowOpacity",
80+
"shadowRadius",
81+
"shadowOffset",
82+
"shadowColor",
83+
"backgroundColor",
84+
"borderColor",
85+
};
86+
if (!kRoutableProperties.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
}
25105

26106
} // namespace reanimated::css

0 commit comments

Comments
 (0)