Add conic gradients to View backgroundImage - #58143
Conversation
|
Warning JavaScript API change detected This PR commits an update to
This change was flagged as: |
866b153 to
178d3e5
Compare
|
Hey @intergalacticspacehighway, who help landing this PR? |
| CGPoint normalizedCenter = CGPointMake(centerPoint.x / size.width, centerPoint.y / size.height); | ||
| CGFloat radians = gradient.from * M_PI / 180.0; | ||
| gradientLayer.startPoint = normalizedCenter; | ||
| gradientLayer.endPoint = CGPointMake(normalizedCenter.x + std::sin(radians), normalizedCenter.y - std::cos(radians)); | ||
|
|
||
| const auto colorStops = [RCTGradientUtils getFixedColorStops:gradient.colorStops gradientLineLength:1.0]; | ||
| NSMutableArray<id> *colors = [NSMutableArray array]; | ||
| NSMutableArray<NSNumber *> *locations = [NSMutableArray array]; | ||
| [RCTGradientUtils getColors:colors andLocations:locations fromColorStops:colorStops]; | ||
|
|
||
| gradientLayer.frame = CGRectMake(0.0f, 0.0f, size.width, size.height); | ||
| gradientLayer.colors = colors; | ||
| gradientLayer.locations = locations; | ||
| return gradientLayer; |
There was a problem hiding this comment.
| CGPoint normalizedCenter = CGPointMake(centerPoint.x / size.width, centerPoint.y / size.height); | |
| CGFloat radians = gradient.from * M_PI / 180.0; | |
| gradientLayer.startPoint = normalizedCenter; | |
| gradientLayer.endPoint = CGPointMake(normalizedCenter.x + std::sin(radians), normalizedCenter.y - std::cos(radians)); | |
| const auto colorStops = [RCTGradientUtils getFixedColorStops:gradient.colorStops gradientLineLength:1.0]; | |
| NSMutableArray<id> *colors = [NSMutableArray array]; | |
| NSMutableArray<NSNumber *> *locations = [NSMutableArray array]; | |
| [RCTGradientUtils getColors:colors andLocations:locations fromColorStops:colorStops]; | |
| gradientLayer.frame = CGRectMake(0.0f, 0.0f, size.width, size.height); | |
| gradientLayer.colors = colors; | |
| gradientLayer.locations = locations; | |
| return gradientLayer; | |
| // CAGradientLayer evaluates a conic sweep in unit coordinate space, which is scaled by the | |
| // layer's width and height. On a non-square layer that skews every angle by W/H, so the | |
| // gradient layer must be square. A square of side max(w, h) anchored at the origin always | |
| // covers the view box. | |
| CGFloat side = std::max(size.width, size.height); | |
| CGPoint normalizedCenter = CGPointMake(centerPoint.x / side, centerPoint.y / side); | |
| CGFloat radians = gradient.from * M_PI / 180.0; | |
| gradientLayer.startPoint = normalizedCenter; | |
| gradientLayer.endPoint = CGPointMake(normalizedCenter.x + std::sin(radians), normalizedCenter.y - std::cos(radians)); | |
| const auto colorStops = [RCTGradientUtils getFixedColorStops:gradient.colorStops gradientLineLength:1.0]; | |
| NSMutableArray<id> *colors = [NSMutableArray array]; | |
| NSMutableArray<NSNumber *> *locations = [NSMutableArray array]; | |
| [RCTGradientUtils getColors:colors andLocations:locations fromColorStops:colorStops]; | |
| gradientLayer.frame = CGRectMake(0.0f, 0.0f, side, side); | |
| gradientLayer.colors = colors; | |
| gradientLayer.locations = locations; | |
| // The caller tiles this layer with a CAReplicatorLayer stepping by `size`, so the returned | |
| // layer must be exactly `size`. Clip the oversized square inside a container. | |
| CALayer *container = [CALayer layer]; | |
| container.frame = CGRectMake(0.0f, 0.0f, size.width, size.height); | |
| container.masksToBounds = YES; | |
| [container addSublayer:gradientLayer]; | |
| return container; |
To fix the squashing that happens on iOS. This change will make it consistent on iOS, android and web.
Here's the issue when conic gradient is applied to a non-square view. iOS squashes it, so we need to give it a big enough square.
| if (gradient.position.top.has_value()) { | ||
| centerPoint.y = gradient.position.top->resolve(static_cast<float>(size.height)); | ||
| } else if (gradient.position.bottom.has_value()) { | ||
| centerPoint.y = size.height - gradient.position.bottom->resolve(static_cast<float>(size.height)); | ||
| } |
There was a problem hiding this comment.
if we pass conic-gradient(from 0deg at right 10px bottom 20px, ...) the JS emits bottom 20, right 10 but BackgroundImagePropsConversions.cpp pre-sets position.top = 50% and position.left = 50% so bottom and right here would not override those values.
Summary:
TL;DR
Adds
conic-gradient()support toView'sbackgroundImagestyle on iOS and Android.Supported syntax includes:
from <angle>at <position>, including four-value edge offsetsImplementation
ConicGradientgraphics model and serialization.CAGradientLayerwithkCAGradientLayerConicon iOS.SweepGradienton Android.Reviewer guide
Recommended order:
processBackgroundImage.jsCSSBackgroundImage.hConicGradient.h/.cppBackgroundImagePropsConversions.cppRCTConicGradient.mmConicGradient.ktReactNativeApi.d.tsis generated and can be reviewed mechanically.Scope
This PR does not add:
repeating-conic-gradient()in hslChangelog:
[GENERAL] [ADDED] - Support conic gradients in View backgroundImage.
Test Plan:
yarn flow-check— 0 errorsyarn test-generated-typescript— passedyarn fantom packages/react-native/Libraries/StyleSheet/__tests__/processBackgroundPosition-itest.js— 80 tests passedyarn fantom packages/react-native/Libraries/StyleSheet/__tests__/processBackgroundImage-itest.js— 77 tests passedyarn fantom packages/react-native/Libraries/Components/View/__tests__/View-nativeCSSParsing-itest.js— 6 tests passedyarn fantom packages/react-native/Libraries/Components/View/__tests__/View-itest.js— 224 tests passed across native CSS parsing and C++ prop setter flags./gradlew :packages:rn-tester:android:app:assembleDebug— built successfullyfromrotation, and off-centeratpositioningKnown merge-readiness issue
The change introduces public C++ API surface, but C++ API snapshots are not updated. Local generation fails on Android views with an existing duplicate
fmt::v12::basic_appenderparser error. Regenerate those snapshots in the supported Linux CI environment before final review.