Skip to content
This repository was archived by the owner on Nov 11, 2024. It is now read-only.

Commit 1f82867

Browse files
committed
feat: add "Gradient" component
1 parent 1272144 commit 1f82867

File tree

8 files changed

+383
-0
lines changed

8 files changed

+383
-0
lines changed

@types/index.d.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8424,6 +8424,42 @@ declare class SwitchComponent extends React.Component<SwitchProps> {}
84248424
declare const SwitchBase: Constructor<NativeMethodsMixin> & typeof SwitchComponent;
84258425
export class Switch extends SwitchBase {}
84268426

8427+
export interface GradientProps extends ViewProps {
8428+
/** The starting color of the gradient. */
8429+
startColor: string;
8430+
/** The ending color of the gradient. */
8431+
endColor: string;
8432+
/**
8433+
* Where the gradient begins, relative to the view's coordinate space.
8434+
*
8435+
* Defaults to `{x: 0, y: 0.5}`
8436+
*/
8437+
startPoint?: PointPropType;
8438+
/**
8439+
* Where the gradient ends, relative to the view's coordinate space.
8440+
*
8441+
* Defaults to `{x: 1, y: 0.5}`
8442+
*/
8443+
endPoint?: PointPropType;
8444+
/**
8445+
* The interpolation factor of the gradient. This defines how smooth the color transition is.
8446+
*
8447+
* Defaults to `1`, which is linear smoothing.
8448+
*/
8449+
slopeFactor?: number;
8450+
/** If true, the gradient is drawn before the start point. */
8451+
drawsBeforeStart?: boolean;
8452+
/** If true, the gradient is drawn past the end point. */
8453+
drawsAfterEnd?: boolean;
8454+
}
8455+
8456+
/**
8457+
* The `Gradient` component blends two colors smoothly in its background.
8458+
*
8459+
* @platform macos
8460+
*/
8461+
export class Gradient extends React.Component<GradientProps> {}
8462+
84278463
/**
84288464
* The `WindowDrag` component provides an area where the mouse can left-click
84298465
* and then drag in order to move the native `RCTWindow` that contains this view.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* Copyright (c) 2015-present, Alec Larson
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*
9+
* @providesModule Gradient
10+
* @noflow
11+
*/
12+
'use strict';
13+
14+
const React = require('react');
15+
const requireNativeComponent = require('requireNativeComponent');
16+
17+
const RCTGradient = requireNativeComponent('RCTGradient');
18+
19+
const Gradient = React.forwardRef((props, ref) => (
20+
<RCTGradient ref={ref} {...props} />
21+
));
22+
23+
Gradient.displayName = 'Gradient';
24+
25+
module.exports = Gradient;

Libraries/react-native/react-native-implementation.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const ReactNative = {
2626
get DatePickerIOS() { return require('DatePickerIOS'); },
2727
get DrawerLayoutAndroid() { return require('DrawerLayoutAndroid'); },
2828
get FlatList() { return require('FlatList'); },
29+
get Gradient() { return require('Gradient'); },
2930
get Image() { return require('Image'); },
3031
get ImageBackground() { return require('ImageBackground'); },
3132
get ImageEditor() { return require('ImageEditor'); },

React/React.xcodeproj/project.pbxproj

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,10 @@
525525
702B7FFD221C88BB0027174A /* RCTMouseEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 702B7FFB221C88BB0027174A /* RCTMouseEvent.m */; };
526526
702B7FFE221C88CE0027174A /* RCTMouseEvent.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 702B7FFA221C88BB0027174A /* RCTMouseEvent.h */; };
527527
702B7FFF221C88D70027174A /* RCTWindow.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 702B7FF6221C88AF0027174A /* RCTWindow.h */; };
528+
70347E1F22FF447400A4F09B /* RCTGradient.h in Headers */ = {isa = PBXBuildFile; fileRef = 70347E1E22FF447400A4F09B /* RCTGradient.h */; };
529+
70347E2122FF448600A4F09B /* RCTGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 70347E2022FF448600A4F09B /* RCTGradient.m */; };
530+
70347E2322FF44B700A4F09B /* RCTGradientManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 70347E2222FF44B700A4F09B /* RCTGradientManager.m */; };
531+
70347E2522FF44C100A4F09B /* RCTGradientManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 70347E2422FF44BF00A4F09B /* RCTGradientManager.h */; };
528532
705EDE2922107DD0000CAA67 /* Utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 705EDE2722107DD0000CAA67 /* Utils.h */; };
529533
705EDE2A22107DD0000CAA67 /* Utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 705EDE2822107DD0000CAA67 /* Utils.cpp */; };
530534
705EDE2C221082CB000CAA67 /* RCTSurfaceSizeMeasureMode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 705EDE2B221082CB000CAA67 /* RCTSurfaceSizeMeasureMode.mm */; };
@@ -1278,6 +1282,10 @@
12781282
702B7FF7221C88AF0027174A /* RCTWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWindow.m; sourceTree = "<group>"; };
12791283
702B7FFA221C88BB0027174A /* RCTMouseEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMouseEvent.h; sourceTree = "<group>"; };
12801284
702B7FFB221C88BB0027174A /* RCTMouseEvent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMouseEvent.m; sourceTree = "<group>"; };
1285+
70347E1E22FF447400A4F09B /* RCTGradient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTGradient.h; sourceTree = "<group>"; };
1286+
70347E2022FF448600A4F09B /* RCTGradient.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTGradient.m; sourceTree = "<group>"; };
1287+
70347E2222FF44B700A4F09B /* RCTGradientManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTGradientManager.m; sourceTree = "<group>"; };
1288+
70347E2422FF44BF00A4F09B /* RCTGradientManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTGradientManager.h; sourceTree = "<group>"; };
12811289
705EDE2722107DD0000CAA67 /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Utils.h; sourceTree = "<group>"; };
12821290
705EDE2822107DD0000CAA67 /* Utils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Utils.cpp; sourceTree = "<group>"; };
12831291
705EDE2B221082CB000CAA67 /* RCTSurfaceSizeMeasureMode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTSurfaceSizeMeasureMode.mm; sourceTree = "<group>"; };
@@ -1636,6 +1644,10 @@
16361644
58C571BF1AA56C1900CDF9C8 /* RCTDatePickerManager.m */,
16371645
3D37B5801D522B190042D5B5 /* RCTFont.h */,
16381646
3D37B5811D522B190042D5B5 /* RCTFont.mm */,
1647+
70347E1E22FF447400A4F09B /* RCTGradient.h */,
1648+
70347E2022FF448600A4F09B /* RCTGradient.m */,
1649+
70347E2422FF44BF00A4F09B /* RCTGradientManager.h */,
1650+
70347E2222FF44B700A4F09B /* RCTGradientManager.m */,
16391651
70D4B5142217707C0007C3F1 /* RCTLayout.h */,
16401652
70D4B5152217707C0007C3F1 /* RCTLayout.m */,
16411653
66CD94AD1F1045E700CB3C7C /* RCTMaskedView.h */,
@@ -2234,6 +2246,7 @@
22342246
130443A11E3FEAA900D93A67 /* RCTFollyConvert.h in Headers */,
22352247
3D80DA1B1DF820620028D040 /* RCTResizeMode.h in Headers */,
22362248
3D80DA1C1DF820620028D040 /* RCTLinkingManager.h in Headers */,
2249+
70347E2522FF44C100A4F09B /* RCTGradientManager.h in Headers */,
22372250
3D80DA1D1DF820620028D040 /* RCTNetworking.h in Headers */,
22382251
3D80DA1E1DF820620028D040 /* RCTNetworkTask.h in Headers */,
22392252
657734841EE834C900A0E9EA /* RCTInspectorDevServerHelper.h in Headers */,
@@ -2383,6 +2396,7 @@
23832396
3D80DA8D1DF820620028D040 /* RCTView.h in Headers */,
23842397
3D80DA8E1DF820620028D040 /* RCTViewControllerProtocol.h in Headers */,
23852398
590D7BFD1EBD458B00D8A370 /* RCTShadowView+Layout.h in Headers */,
2399+
70347E1F22FF447400A4F09B /* RCTGradient.h in Headers */,
23862400
3D80DA8F1DF820620028D040 /* RCTViewManager.h in Headers */,
23872401
13134CA01E296B2A00B9F3CB /* RCTCxxUtils.h in Headers */,
23882402
599FAA4A1FB274980058CCF6 /* RCTSurfaceView.h in Headers */,
@@ -2830,9 +2844,11 @@
28302844
EBF21BBD1FC498270052F4D5 /* InspectorInterfaces.cpp in Sources */,
28312845
59EDBCAF1FDF4E0C003573DE /* RCTScrollContentView.m in Sources */,
28322846
135A9C001E7B0EE600587AEB /* RCTJSCHelpers.mm in Sources */,
2847+
70347E2122FF448600A4F09B /* RCTGradient.m in Sources */,
28332848
B233E6EA1D2D845D00BC68BA /* RCTI18nManager.m in Sources */,
28342849
3D7BFD1F1EA8E351008DFB7A /* RCTPackagerConnection.mm in Sources */,
28352850
13456E931ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m in Sources */,
2851+
70347E2322FF44B700A4F09B /* RCTGradientManager.m in Sources */,
28362852
D49593DF202C937C00A7694B /* RCTCursorManager.m in Sources */,
28372853
13A1F71E1A75392D00D3D453 /* RCTKeyCommands.m in Sources */,
28382854
83CBBA531A601E3B00E9B192 /* RCTUtils.m in Sources */,

React/Views/RCTGradient.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// The MIT License (MIT)
2+
//
3+
// Copyright (c) 2014 Jernej Strasner
4+
//
5+
// Permission is hereby granted, free of charge, to any person obtaining a copy of
6+
// this software and associated documentation files (the "Software"), to deal in
7+
// the Software without restriction, including without limitation the rights to
8+
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9+
// the Software, and to permit persons to whom the Software is furnished to do so,
10+
// subject to the following conditions:
11+
//
12+
// The above copyright notice and this permission notice shall be included in all
13+
// copies or substantial portions of the Software.
14+
//
15+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17+
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18+
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19+
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20+
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21+
22+
#import <React/RCTView.h>
23+
24+
@interface RCTGradient : RCTView
25+
26+
/// The starting color of the gradient.
27+
@property (nonatomic, nonnull) NSColor *startColor;
28+
29+
/// The ending color of the gradient.
30+
@property (nonatomic, nonnull) NSColor *endColor;
31+
32+
/// The interpolation factor of the gradient. This defines how smooth the color transition is.
33+
@property (nonatomic) CGFloat slopeFactor;
34+
35+
/// If YES the gradient is drawn before the start point.
36+
@property (nonatomic) BOOL drawsBeforeStart;
37+
38+
/// If YES the gradient is drawn past the end point.
39+
@property (nonatomic) BOOL drawsAfterEnd;
40+
41+
/// The location of the gradient drawing start relative to the view's coordinate space (0.0 - 1.0).
42+
@property (nonatomic) CGPoint startPoint;
43+
44+
/// The location of the gradient drawing end relative to the view's coordinate space (0.0 - 1.0).
45+
@property (nonatomic) CGPoint endPoint;
46+
47+
@end

React/Views/RCTGradient.m

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
// The MIT License (MIT)
2+
//
3+
// Copyright (c) 2014 Jernej Strasner
4+
//
5+
// Permission is hereby granted, free of charge, to any person obtaining a copy of
6+
// this software and associated documentation files (the "Software"), to deal in
7+
// the Software without restriction, including without limitation the rights to
8+
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9+
// the Software, and to permit persons to whom the Software is furnished to do so,
10+
// subject to the following conditions:
11+
//
12+
// The above copyright notice and this permission notice shall be included in all
13+
// copies or substantial portions of the Software.
14+
//
15+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17+
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18+
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19+
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20+
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21+
22+
#import "RCTGradient.h"
23+
24+
#import "RCTLog.h"
25+
#import "RCTUtils.h"
26+
#import "UIImageUtils.h"
27+
28+
// Info struct to pass to shading function
29+
struct _JSTFunctionInfo {
30+
CGFloat startColor[4];
31+
CGFloat endColor[4];
32+
CGFloat slopeFactor;
33+
};
34+
typedef struct _JSTFunctionInfo JSTFunctionInfo;
35+
typedef struct _JSTFunctionInfo* JSTFunctionInfoRef;
36+
37+
static JSTFunctionInfoRef JSTFunctionInfoCreate() {
38+
return (JSTFunctionInfoRef)malloc(sizeof(JSTFunctionInfo));
39+
}
40+
41+
static void JSTFunctionInfoRelease(JSTFunctionInfoRef info) {
42+
if (info != NULL) {
43+
free(info);
44+
}
45+
}
46+
47+
// Distributes values on a slope aka. ease-in ease-out
48+
static float JSTSlope(float x, float A)
49+
{
50+
float p = powf(x, A);
51+
return p/(p + powf(1.0f-x, A));
52+
}
53+
54+
// This is the callback of our shading function.
55+
// info: color and slope information
56+
// inData: contains a single float that gives is the current position within the gradient
57+
// outData: we fill this with the color to display at the given position
58+
static void JSTShadingFunction(void *infoPtr, const CGFloat *inData, CGFloat *outData)
59+
{
60+
JSTFunctionInfo info = *(JSTFunctionInfo*)infoPtr; // Info struct with colors and parameters
61+
float p = inData[0]; // Position in gradient
62+
float q = JSTSlope(p, info.slopeFactor); // Slope value
63+
outData[0] = info.startColor[0] + (info.endColor[0] - info.startColor[0])*q;
64+
outData[1] = info.startColor[1] + (info.endColor[1] - info.startColor[1])*q;
65+
outData[2] = info.startColor[2] + (info.endColor[2] - info.startColor[2])*q;
66+
outData[3] = info.startColor[3] + (info.endColor[3] - info.startColor[3])*q;
67+
}
68+
69+
@implementation RCTGradient
70+
{
71+
CGColorSpaceRef _colorSpace;
72+
CGFloat _startColorComps[4];
73+
CGFloat _endColorComps[4];
74+
CGFunctionRef _function;
75+
JSTFunctionInfoRef _functionInfo;
76+
BOOL _opaque;
77+
}
78+
79+
- (instancetype)init
80+
{
81+
return [self initWithFrame:CGRectZero];
82+
}
83+
84+
- (instancetype)initWithFrame:(CGRect)frame
85+
{
86+
if (self = [super initWithFrame:frame]) {
87+
_colorSpace = CGColorSpaceCreateDeviceRGB();
88+
_startColor = [NSColor clearColor];
89+
_endColor = [NSColor clearColor];
90+
_startPoint = CGPointMake(0, 0.5);
91+
_endPoint = CGPointMake(1, 0.5);
92+
_slopeFactor = 1;
93+
}
94+
return self;
95+
}
96+
97+
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:unused)
98+
99+
- (void)dealloc
100+
{
101+
JSTFunctionInfoRelease(_functionInfo);
102+
CGFunctionRelease(_function);
103+
CGColorSpaceRelease(_colorSpace);
104+
}
105+
106+
- (void)setStartColor:(NSColor *)startColor
107+
{
108+
if (_startColor == startColor) return;
109+
_startColor = startColor;
110+
111+
[startColor getRed:_startColorComps green:_startColorComps+1 blue:_startColorComps+2 alpha:_startColorComps+3];
112+
}
113+
114+
- (void)setEndColor:(NSColor *)endColor
115+
{
116+
if (_endColor == endColor) return;
117+
_endColor = endColor;
118+
119+
[endColor getRed:_endColorComps green:_endColorComps+1 blue:_endColorComps+2 alpha:_endColorComps+3];
120+
}
121+
122+
- (void)didSetProps:(NSArray<NSString *> *)changedProps
123+
{
124+
for (NSString *prop in changedProps) {
125+
if ([prop isEqualToString:@"startColor"] ||
126+
[prop isEqualToString:@"endColor"] ||
127+
[prop isEqualToString:@"slopeFactor"]) {
128+
[self _createShadingFunction];
129+
break;
130+
}
131+
}
132+
[self setNeedsDisplay:YES];
133+
}
134+
135+
- (BOOL)isOpaque
136+
{
137+
return _endColorComps[3] == 1 && _startColorComps[3] == 1;
138+
}
139+
140+
- (void)_createShadingFunction
141+
{
142+
// Shading function info
143+
JSTFunctionInfoRelease(_functionInfo);
144+
_functionInfo = JSTFunctionInfoCreate();
145+
memcpy(_functionInfo->startColor, _startColorComps, sizeof(CGFloat)*4);
146+
memcpy(_functionInfo->endColor, _endColorComps, sizeof(CGFloat)*4);
147+
_functionInfo->slopeFactor = _slopeFactor;
148+
149+
// Define the shading callbacks
150+
CGFunctionCallbacks callbacks = {0, JSTShadingFunction, NULL};
151+
152+
// As input to our function we want 1 value in the range [0.0, 1.0].
153+
// This is our position within the gradient.
154+
size_t domainDimension = 1;
155+
CGFloat domain[2] = {0.0f, 1.0f};
156+
157+
// The output of our shading function are 4 values, each in the range [0.0, 1.0].
158+
// By specifying 4 ranges here, we limit each color component to that range. Values outside of the range get clipped.
159+
size_t rangeDimension = 4;
160+
CGFloat range[8] = {
161+
0.0f, 1.0f, // R
162+
0.0f, 1.0f, // G
163+
0.0f, 1.0f, // B
164+
0.0f, 1.0f // A
165+
};
166+
167+
// Create the shading function
168+
CGFunctionRelease(_function);
169+
_function = CGFunctionCreate(_functionInfo, domainDimension, domain, rangeDimension, range, &callbacks);
170+
}
171+
172+
- (void)displayLayer:(CALayer *)layer
173+
{
174+
[super displayLayer:layer];
175+
176+
const CGSize size = self.bounds.size;
177+
UIGraphicsBeginImageContextWithOptions(size, self.isOpaque, 0.0);
178+
179+
// Preserve contents from super call.
180+
NSImage *contents = layer.contents;
181+
[contents drawInRect:self.bounds];
182+
183+
// Draw the gradient.
184+
[self drawRect:self.bounds];
185+
186+
// Fetch the drawing bitmap.
187+
contents = UIGraphicsGetImageFromCurrentImageContext();
188+
UIGraphicsEndImageContext();
189+
190+
layer.contents = contents;
191+
layer.magnificationFilter = kCAFilterNearest;
192+
layer.needsDisplayOnBoundsChange = YES;
193+
}
194+
195+
- (void)drawRect:(CGRect)rect
196+
{
197+
// Prepare general variables
198+
CGContextRef context = UIGraphicsGetCurrentContext();
199+
200+
// Create the shading object
201+
CGPoint startPoint = CGPointMake(_startPoint.x * rect.size.width, _startPoint.y * rect.size.height);
202+
CGPoint endPoint = CGPointMake(_endPoint.x * rect.size.width, _endPoint.y * rect.size.height);
203+
CGShadingRef shading = CGShadingCreateAxial(_colorSpace, startPoint, endPoint, _function, _drawsBeforeStart, _drawsAfterEnd);
204+
205+
// Draw the shading
206+
CGContextDrawShading(context, shading);
207+
208+
// Clean up
209+
CGShadingRelease(shading);
210+
}
211+
212+
@end

0 commit comments

Comments
 (0)