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>
711#import < cstdint>
12+ #import < memory>
813#import < string>
14+ #import < string_view>
915#import < unordered_map>
1016#import < variant>
1117
1218namespace reanimated ::css {
1319
20+ using namespace facebook ::react;
21+
1422namespace {
1523
1624// Defined here with the rest of the parse machinery; first consumed once color
3341{
3442 static const std::unordered_map<std::string, CSSPropertyTraits> kProperties = {
3543 {" opacity" , {CSSValueKind::Scalar, 1.0 }},
44+ {" shadowOpacity" , {CSSValueKind::Scalar, 1.0 }},
45+ {" shadowRadius" , {CSSValueKind::Scalar, 0.0 }},
46+ {" shadowOffset" , {CSSValueKind::Size, std::array<double , 2 >{0 , 0 }}},
47+ {" shadowColor" , {CSSValueKind::Color, kBlackColor }},
48+ {" backgroundColor" , {CSSValueKind::Color, kTransparentColor }},
49+ {" borderColor" , {CSSValueKind::Color, kBlackColor }},
3650 };
3751 const auto it = kProperties .find (propertyName);
3852 return it != kProperties .end () ? &it->second : nullptr ;
@@ -60,13 +74,57 @@ CGColorSpaceRef sharedSRGBColorSpace()
6074 return space;
6175}
6276
77+ // Per-side colors subvert the uniform borderColor when they transition with
78+ // it - RN then draws the border through its custom path, where main-layer
79+ // animations are invisible.
80+ bool hasPerSideBorderColorSibling (const std::unordered_set<std::string> &transitioningProperties)
81+ {
82+ static constexpr std::array<std::string_view, 9 > kSiblings = {
83+ " borderTopColor" ,
84+ " borderRightColor" ,
85+ " borderBottomColor" ,
86+ " borderLeftColor" ,
87+ " borderStartColor" ,
88+ " borderEndColor" ,
89+ " borderBlockColor" ,
90+ " borderBlockStartColor" ,
91+ " borderBlockEndColor" ,
92+ };
93+ for (const auto &sibling : kSiblings ) {
94+ if (transitioningProperties.contains (std::string (sibling))) {
95+ return true ;
96+ }
97+ }
98+ return false ;
99+ }
100+
101+ // Mirrors useCoreAnimationBorderRendering in RN's RCTViewComponentView: when
102+ // it is false, RN draws the background and border into dedicated sublayers
103+ // that are rebuilt (and stripped of animations) on every commit, so main-layer
104+ // animations of those properties would be invisible.
105+ bool usesCoreAnimationBorderRendering (const ShadowNode &shadowNode)
106+ {
107+ const auto *layoutableShadowNode = dynamic_cast <const LayoutableShadowNode *>(&shadowNode);
108+ if (layoutableShadowNode == nullptr ) {
109+ return false ;
110+ }
111+ const auto viewProps = std::static_pointer_cast<const ViewProps>(shadowNode.getProps ());
112+ const auto borderMetrics = viewProps->resolveBorderMetrics (layoutableShadowNode->getLayoutMetrics ());
113+ return borderMetrics.borderColors .isUniform () && borderMetrics.borderWidths .isUniform () &&
114+ borderMetrics.borderStyles .isUniform () && borderMetrics.borderStyles .left == BorderStyle::Solid &&
115+ borderMetrics.borderRadii .isUniform () &&
116+ (borderMetrics.borderWidths .left == 0 || viewProps->getClipsContentToBounds () ||
117+ (colorComponentsFromColor (borderMetrics.borderColors .left ).alpha == 0 &&
118+ (*borderMetrics.borderColors .left ).getUIColor () != nullptr ));
119+ }
120+
63121} // namespace
64122
65123bool canRouteCSSProperty (
66124 const std::string &propertyName,
67125 const EasingConfig &easing,
68- const facebook::react:: ShadowNode & /* shadowNode*/ ,
69- const std::unordered_set<std::string> & /* transitioningProperties*/ )
126+ const ShadowNode &shadowNode,
127+ const std::unordered_set<std::string> &transitioningProperties)
70128{
71129 if constexpr (!StaticFeatureFlags::getFlag (" IOS_CSS_CORE_ANIMATION" )) {
72130 return false ;
@@ -76,7 +134,19 @@ bool canRouteCSSProperty(
76134 }
77135 // CAMediaTimingFunction can express only linear and cubic-bezier curves;
78136 // steps / linear-stops easings have to interpolate per-frame on the loop.
79- return std::holds_alternative<LinearEasing>(easing) || std::holds_alternative<CubicBezierEasing>(easing);
137+ if (!std::holds_alternative<LinearEasing>(easing) && !std::holds_alternative<CubicBezierEasing>(easing)) {
138+ return false ;
139+ }
140+ // Background and border colors live on the main layer only while RN uses
141+ // Core Animation border rendering for this view.
142+ if ((propertyName == " backgroundColor" || propertyName == " borderColor" ) &&
143+ !usesCoreAnimationBorderRendering (shadowNode)) {
144+ return false ;
145+ }
146+ if (propertyName == " borderColor" && hasPerSideBorderColorSibling (transitioningProperties)) {
147+ return false ;
148+ }
149+ return true ;
80150}
81151
82152std::optional<PlatformValue> parsePlatformValue (const std::string &propertyName, const CSSEndpointValue &value)
0 commit comments