mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
feat: linear gradient ios (#45434)
Summary: - Adds `background` prop that supports CSS's linear gradient. Later this can be extended to support various other gradients and possibly CSS's background image (less motivation as better solutions exists for image) - Uses `CAGradientlayer` to draw Linear Gradient layers. So it is GPU optimised under the hood. - Style supports JS object to specify `LinearGradient`, so it can support Animated libraries. ## Changelog: [IOS] [ADDED] - linear gradient <!-- Help reviewers and the release process by writing your own changelog entry. Pick one each for the category and type tags: [ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message For more details, see: https://reactnative.dev/contributing/changelogs-in-pull-requests Pull Request resolved: https://github.com/facebook/react-native/pull/45434 Test Plan: - Check out `processBackground-test.js` for supported syntax testcases. - Checkout example added in ViewExample.js Although the PR is tested well but open to any changes/feedback on the approach taken. Android PR - https://github.com/facebook/react-native/pull/45433. Separated the PRs to keep it easier to review. Both PRs can be reviewed individually. Reviewed By: NickGerleman Differential Revision: D60791581 Pulled By: joevilches fbshipit-source-id: 051088fdf68d9fe20c0c306f1f1c591cbd77f3d5
This commit is contained in:
committed by
Facebook GitHub Bot
parent
09fd7528b6
commit
b99675d78a
+68
@@ -33,6 +33,7 @@ using namespace facebook::react;
|
||||
__weak CALayer *_borderLayer;
|
||||
CALayer *_boxShadowLayer;
|
||||
CALayer *_filterLayer;
|
||||
NSMutableArray<CAGradientLayer *> *_gradientLayers;
|
||||
BOOL _needsInvalidateLayer;
|
||||
BOOL _isJSResponder;
|
||||
BOOL _removeClippedSubviews;
|
||||
@@ -458,6 +459,11 @@ using namespace facebook::react;
|
||||
}
|
||||
}
|
||||
|
||||
// `linearGradient`
|
||||
if (oldViewProps.backgroundImage != newViewProps.backgroundImage) {
|
||||
needsInvalidateLayer = YES;
|
||||
}
|
||||
|
||||
// `boxShadow`
|
||||
if (oldViewProps.boxShadow != newViewProps.boxShadow) {
|
||||
needsInvalidateLayer = YES;
|
||||
@@ -830,6 +836,56 @@ static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle)
|
||||
[self.layer addSublayer:_filterLayer];
|
||||
}
|
||||
|
||||
[self clearExistingGradientLayers];
|
||||
auto backgroundImage = _props->backgroundImage;
|
||||
if (backgroundImage.empty()) {
|
||||
return;
|
||||
}
|
||||
for (const auto &gradient : backgroundImage) {
|
||||
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
|
||||
NSMutableArray *colors = [NSMutableArray array];
|
||||
NSMutableArray *locations = [NSMutableArray array];
|
||||
for (const auto &colorStop : gradient.colorStops) {
|
||||
if (colorStop.position.has_value()) {
|
||||
auto location = @(colorStop.position.value());
|
||||
UIColor *color = RCTUIColorFromSharedColor(colorStop.color);
|
||||
[colors addObject:(id)color.CGColor];
|
||||
[locations addObject:location];
|
||||
}
|
||||
}
|
||||
gradientLayer.startPoint = CGPointMake(gradient.startX, gradient.startY);
|
||||
gradientLayer.endPoint = CGPointMake(gradient.endX, gradient.endY);
|
||||
|
||||
if (locations.count > 0) {
|
||||
gradientLayer.locations = locations;
|
||||
}
|
||||
gradientLayer.colors = colors;
|
||||
gradientLayer.frame = layer.bounds;
|
||||
|
||||
// border styling to work with gradient layers
|
||||
if (useCoreAnimationBorderRendering) {
|
||||
gradientLayer.borderWidth = layer.borderWidth;
|
||||
gradientLayer.borderColor = layer.borderColor;
|
||||
gradientLayer.cornerRadius = layer.cornerRadius;
|
||||
gradientLayer.cornerCurve = layer.cornerCurve;
|
||||
} else {
|
||||
CAShapeLayer *maskLayer = [CAShapeLayer layer];
|
||||
CGPathRef path = RCTPathCreateWithRoundedRect(
|
||||
self.bounds,
|
||||
RCTGetCornerInsets(RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii), UIEdgeInsetsZero),
|
||||
nil);
|
||||
maskLayer.path = path;
|
||||
CGPathRelease(path);
|
||||
gradientLayer.mask = maskLayer;
|
||||
}
|
||||
|
||||
// border layer should appear above gradient layers to make sure that the border is visible
|
||||
gradientLayer.zPosition = _borderLayer.zPosition - 1;
|
||||
|
||||
[self.layer addSublayer:gradientLayer];
|
||||
[_gradientLayers addObject:gradientLayer];
|
||||
}
|
||||
|
||||
[_boxShadowLayer removeFromSuperlayer];
|
||||
_boxShadowLayer = nil;
|
||||
if (!_props->boxShadow.empty()) {
|
||||
@@ -848,6 +904,18 @@ static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle)
|
||||
}
|
||||
}
|
||||
|
||||
- (void)clearExistingGradientLayers
|
||||
{
|
||||
if (_gradientLayers == nil) {
|
||||
_gradientLayers = [NSMutableArray new];
|
||||
return;
|
||||
}
|
||||
for (CAGradientLayer *gradientLayer in _gradientLayers) {
|
||||
[gradientLayer removeFromSuperlayer];
|
||||
}
|
||||
[_gradientLayers removeAllObjects];
|
||||
}
|
||||
|
||||
#pragma mark - Accessibility
|
||||
|
||||
- (NSObject *)accessibilityElement
|
||||
|
||||
@@ -126,4 +126,35 @@ exports.examples = [
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Gradient with uniform border style',
|
||||
render(): React.Node {
|
||||
return (
|
||||
<GradientBox
|
||||
testID="linear-gradient-with-uniform-borders"
|
||||
style={{
|
||||
experimental_backgroundImage:
|
||||
'linear-gradient(to bottom right, yellow, green);',
|
||||
borderRadius: 16,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Gradient with non-uniform border style',
|
||||
render(): React.Node {
|
||||
return (
|
||||
<GradientBox
|
||||
testID="linear-gradient-with-non-uniform-borders"
|
||||
style={{
|
||||
experimental_backgroundImage:
|
||||
'linear-gradient(to bottom right, yellow, green);',
|
||||
borderTopRightRadius: 8,
|
||||
borderTopLeftRadius: 80,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -298,6 +298,10 @@ const APIs: Array<RNTesterModuleInfo> = ([
|
||||
key: 'FilterExample',
|
||||
module: require('../examples/Filter/FilterExample'),
|
||||
},
|
||||
{
|
||||
key: 'LinearGradient',
|
||||
module: require('../examples/LinearGradient/LinearGradientExample'),
|
||||
},
|
||||
{
|
||||
key: 'MixBlendModeExample',
|
||||
module: require('../examples/MixBlendMode/MixBlendModeExample'),
|
||||
|
||||
Reference in New Issue
Block a user