|
2 | 2 |
|
3 | 3 | #import <reanimated/Tools/FeatureFlags.h> |
4 | 4 |
|
| 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> |
5 | 13 | #import <variant> |
6 | 14 |
|
7 | 15 | namespace reanimated::css { |
8 | 16 |
|
| 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 | + |
9 | 78 | bool canRouteCSSProperty( |
10 | 79 | const std::string &propertyName, |
11 | 80 | 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) |
14 | 83 | { |
15 | 84 | if constexpr (!StaticFeatureFlags::getFlag("IOS_CSS_CORE_ANIMATION")) { |
16 | 85 | return false; |
17 | 86 | } |
18 | | - if (propertyName != "opacity") { |
| 87 | + if (!kRoutableProperties.contains(propertyName)) { |
19 | 88 | return false; |
20 | 89 | } |
21 | 90 | // CAMediaTimingFunction can express only linear and cubic-bezier curves; |
22 | 91 | // 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; |
24 | 105 | } |
25 | 106 |
|
26 | 107 | } // namespace reanimated::css |
0 commit comments