33#import < reanimated/CSS/utils/props.h>
44#import < reanimated/Tools/FeatureFlags.h>
55
6+ #import < react/renderer/components/view/ViewProps.h>
7+ #import < react/renderer/core/LayoutableShadowNode.h>
8+ #import < react/renderer/graphics/Color.h>
9+
610#import < array>
11+ #import < memory>
712#import < string>
13+ #import < string_view>
814#import < unordered_map>
915#import < variant>
1016
1117namespace reanimated ::css {
1218
19+ using namespace facebook ::react;
20+
1321namespace {
1422
1523constexpr std::array<double , 4 > kTransparentColor = {0 , 0 , 0 , 0 };
3038{
3139 static const std::unordered_map<std::string, CSSPropertyTraits> kProperties = {
3240 {" opacity" , {CSSValueKind::Scalar, 1.0 }},
41+ {" shadowOpacity" , {CSSValueKind::Scalar, 1.0 }},
42+ {" shadowRadius" , {CSSValueKind::Scalar, 0.0 }},
43+ {" shadowOffset" , {CSSValueKind::Size, std::array<double , 2 >{0 , 0 }}},
44+ {" shadowColor" , {CSSValueKind::Color, kBlackColor }},
45+ {" backgroundColor" , {CSSValueKind::Color, kTransparentColor }},
46+ {" borderColor" , {CSSValueKind::Color, kBlackColor }},
3347 };
3448 const auto it = kProperties .find (propertyName);
3549 return it != kProperties .end () ? &it->second : nullptr ;
@@ -57,13 +71,57 @@ CGColorSpaceRef sharedSRGBColorSpace()
5771 return space;
5872}
5973
74+ // Per-side colors subvert the uniform borderColor when they transition with
75+ // it - RN then draws the border through its custom path, where main-layer
76+ // animations are invisible.
77+ bool hasPerSideBorderColorSibling (const std::unordered_set<std::string> &transitioningProperties)
78+ {
79+ static constexpr std::array<std::string_view, 9 > kSiblings = {
80+ " borderTopColor" ,
81+ " borderRightColor" ,
82+ " borderBottomColor" ,
83+ " borderLeftColor" ,
84+ " borderStartColor" ,
85+ " borderEndColor" ,
86+ " borderBlockColor" ,
87+ " borderBlockStartColor" ,
88+ " borderBlockEndColor" ,
89+ };
90+ for (const auto &sibling : kSiblings ) {
91+ if (transitioningProperties.contains (std::string (sibling))) {
92+ return true ;
93+ }
94+ }
95+ return false ;
96+ }
97+
98+ // Mirrors useCoreAnimationBorderRendering in RN's RCTViewComponentView: when
99+ // it is false, RN draws the background and border into dedicated sublayers
100+ // that are rebuilt (and stripped of animations) on every commit, so main-layer
101+ // animations of those properties would be invisible.
102+ bool usesCoreAnimationBorderRendering (const ShadowNode &shadowNode)
103+ {
104+ const auto *layoutableShadowNode = dynamic_cast <const LayoutableShadowNode *>(&shadowNode);
105+ if (layoutableShadowNode == nullptr ) {
106+ return false ;
107+ }
108+ const auto viewProps = std::static_pointer_cast<const ViewProps>(shadowNode.getProps ());
109+ const auto borderMetrics = viewProps->resolveBorderMetrics (layoutableShadowNode->getLayoutMetrics ());
110+ return borderMetrics.borderColors .isUniform () && borderMetrics.borderWidths .isUniform () &&
111+ borderMetrics.borderStyles .isUniform () && borderMetrics.borderStyles .left == BorderStyle::Solid &&
112+ borderMetrics.borderRadii .isUniform () &&
113+ (borderMetrics.borderWidths .left == 0 || viewProps->getClipsContentToBounds () ||
114+ (colorComponentsFromColor (borderMetrics.borderColors .left ).alpha == 0 &&
115+ (*borderMetrics.borderColors .left ).getUIColor () != nullptr ));
116+ }
117+
60118} // namespace
61119
62120bool canRouteCSSProperty (
63121 const std::string &propertyName,
64122 const EasingConfig &easing,
65- const facebook::react:: ShadowNode & /* shadowNode*/ ,
66- const std::unordered_set<std::string> & /* transitioningProperties*/ )
123+ const ShadowNode &shadowNode,
124+ const std::unordered_set<std::string> &transitioningProperties)
67125{
68126 if constexpr (!StaticFeatureFlags::getFlag (" IOS_CSS_CORE_ANIMATION" )) {
69127 return false ;
@@ -73,7 +131,19 @@ bool canRouteCSSProperty(
73131 }
74132 // CAMediaTimingFunction can express only linear and cubic-bezier curves;
75133 // steps / linear-stops easings have to interpolate per-frame on the loop.
76- return std::holds_alternative<LinearEasing>(easing) || std::holds_alternative<CubicBezierEasing>(easing);
134+ if (!std::holds_alternative<LinearEasing>(easing) && !std::holds_alternative<CubicBezierEasing>(easing)) {
135+ return false ;
136+ }
137+ // Background and border colors live on the main layer only while RN uses
138+ // Core Animation border rendering for this view.
139+ if ((propertyName == " backgroundColor" || propertyName == " borderColor" ) &&
140+ !usesCoreAnimationBorderRendering (shadowNode)) {
141+ return false ;
142+ }
143+ if (propertyName == " borderColor" && hasPerSideBorderColorSibling (transitioningProperties)) {
144+ return false ;
145+ }
146+ return true ;
77147}
78148
79149std::optional<PlatformValue>
0 commit comments