Skip to content

Commit

Permalink
feat: add "Gradient" component
Browse files Browse the repository at this point in the history
  • Loading branch information
aleclarson committed Aug 11, 2019
1 parent 1272144 commit 1f82867
Show file tree
Hide file tree
Showing 8 changed files with 383 additions and 0 deletions.
36 changes: 36 additions & 0 deletions @types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8424,6 +8424,42 @@ declare class SwitchComponent extends React.Component<SwitchProps> {}
declare const SwitchBase: Constructor<NativeMethodsMixin> & typeof SwitchComponent;
export class Switch extends SwitchBase {}

export interface GradientProps extends ViewProps {
/** The starting color of the gradient. */
startColor: string;
/** The ending color of the gradient. */
endColor: string;
/**
* Where the gradient begins, relative to the view's coordinate space.
*
* Defaults to `{x: 0, y: 0.5}`
*/
startPoint?: PointPropType;
/**
* Where the gradient ends, relative to the view's coordinate space.
*
* Defaults to `{x: 1, y: 0.5}`
*/
endPoint?: PointPropType;
/**
* The interpolation factor of the gradient. This defines how smooth the color transition is.
*
* Defaults to `1`, which is linear smoothing.
*/
slopeFactor?: number;
/** If true, the gradient is drawn before the start point. */
drawsBeforeStart?: boolean;
/** If true, the gradient is drawn past the end point. */
drawsAfterEnd?: boolean;
}

/**
* The `Gradient` component blends two colors smoothly in its background.
*
* @platform macos
*/
export class Gradient extends React.Component<GradientProps> {}

/**
* The `WindowDrag` component provides an area where the mouse can left-click
* and then drag in order to move the native `RCTWindow` that contains this view.
Expand Down
25 changes: 25 additions & 0 deletions Libraries/Components/Gradient/Gradient.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Copyright (c) 2015-present, Alec Larson
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule Gradient
* @noflow
*/
'use strict';

const React = require('react');
const requireNativeComponent = require('requireNativeComponent');

const RCTGradient = requireNativeComponent('RCTGradient');

const Gradient = React.forwardRef((props, ref) => (
<RCTGradient ref={ref} {...props} />
));

Gradient.displayName = 'Gradient';

module.exports = Gradient;
1 change: 1 addition & 0 deletions Libraries/react-native/react-native-implementation.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const ReactNative = {
get DatePickerIOS() { return require('DatePickerIOS'); },
get DrawerLayoutAndroid() { return require('DrawerLayoutAndroid'); },
get FlatList() { return require('FlatList'); },
get Gradient() { return require('Gradient'); },
get Image() { return require('Image'); },
get ImageBackground() { return require('ImageBackground'); },
get ImageEditor() { return require('ImageEditor'); },
Expand Down
16 changes: 16 additions & 0 deletions React/React.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,10 @@
702B7FFD221C88BB0027174A /* RCTMouseEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 702B7FFB221C88BB0027174A /* RCTMouseEvent.m */; };
702B7FFE221C88CE0027174A /* RCTMouseEvent.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 702B7FFA221C88BB0027174A /* RCTMouseEvent.h */; };
702B7FFF221C88D70027174A /* RCTWindow.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 702B7FF6221C88AF0027174A /* RCTWindow.h */; };
70347E1F22FF447400A4F09B /* RCTGradient.h in Headers */ = {isa = PBXBuildFile; fileRef = 70347E1E22FF447400A4F09B /* RCTGradient.h */; };
70347E2122FF448600A4F09B /* RCTGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 70347E2022FF448600A4F09B /* RCTGradient.m */; };
70347E2322FF44B700A4F09B /* RCTGradientManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 70347E2222FF44B700A4F09B /* RCTGradientManager.m */; };
70347E2522FF44C100A4F09B /* RCTGradientManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 70347E2422FF44BF00A4F09B /* RCTGradientManager.h */; };
705EDE2922107DD0000CAA67 /* Utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 705EDE2722107DD0000CAA67 /* Utils.h */; };
705EDE2A22107DD0000CAA67 /* Utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 705EDE2822107DD0000CAA67 /* Utils.cpp */; };
705EDE2C221082CB000CAA67 /* RCTSurfaceSizeMeasureMode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 705EDE2B221082CB000CAA67 /* RCTSurfaceSizeMeasureMode.mm */; };
Expand Down Expand Up @@ -1278,6 +1282,10 @@
702B7FF7221C88AF0027174A /* RCTWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWindow.m; sourceTree = "<group>"; };
702B7FFA221C88BB0027174A /* RCTMouseEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMouseEvent.h; sourceTree = "<group>"; };
702B7FFB221C88BB0027174A /* RCTMouseEvent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMouseEvent.m; sourceTree = "<group>"; };
70347E1E22FF447400A4F09B /* RCTGradient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTGradient.h; sourceTree = "<group>"; };
70347E2022FF448600A4F09B /* RCTGradient.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTGradient.m; sourceTree = "<group>"; };
70347E2222FF44B700A4F09B /* RCTGradientManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTGradientManager.m; sourceTree = "<group>"; };
70347E2422FF44BF00A4F09B /* RCTGradientManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTGradientManager.h; sourceTree = "<group>"; };
705EDE2722107DD0000CAA67 /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Utils.h; sourceTree = "<group>"; };
705EDE2822107DD0000CAA67 /* Utils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Utils.cpp; sourceTree = "<group>"; };
705EDE2B221082CB000CAA67 /* RCTSurfaceSizeMeasureMode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTSurfaceSizeMeasureMode.mm; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1636,6 +1644,10 @@
58C571BF1AA56C1900CDF9C8 /* RCTDatePickerManager.m */,
3D37B5801D522B190042D5B5 /* RCTFont.h */,
3D37B5811D522B190042D5B5 /* RCTFont.mm */,
70347E1E22FF447400A4F09B /* RCTGradient.h */,
70347E2022FF448600A4F09B /* RCTGradient.m */,
70347E2422FF44BF00A4F09B /* RCTGradientManager.h */,
70347E2222FF44B700A4F09B /* RCTGradientManager.m */,
70D4B5142217707C0007C3F1 /* RCTLayout.h */,
70D4B5152217707C0007C3F1 /* RCTLayout.m */,
66CD94AD1F1045E700CB3C7C /* RCTMaskedView.h */,
Expand Down Expand Up @@ -2234,6 +2246,7 @@
130443A11E3FEAA900D93A67 /* RCTFollyConvert.h in Headers */,
3D80DA1B1DF820620028D040 /* RCTResizeMode.h in Headers */,
3D80DA1C1DF820620028D040 /* RCTLinkingManager.h in Headers */,
70347E2522FF44C100A4F09B /* RCTGradientManager.h in Headers */,
3D80DA1D1DF820620028D040 /* RCTNetworking.h in Headers */,
3D80DA1E1DF820620028D040 /* RCTNetworkTask.h in Headers */,
657734841EE834C900A0E9EA /* RCTInspectorDevServerHelper.h in Headers */,
Expand Down Expand Up @@ -2383,6 +2396,7 @@
3D80DA8D1DF820620028D040 /* RCTView.h in Headers */,
3D80DA8E1DF820620028D040 /* RCTViewControllerProtocol.h in Headers */,
590D7BFD1EBD458B00D8A370 /* RCTShadowView+Layout.h in Headers */,
70347E1F22FF447400A4F09B /* RCTGradient.h in Headers */,
3D80DA8F1DF820620028D040 /* RCTViewManager.h in Headers */,
13134CA01E296B2A00B9F3CB /* RCTCxxUtils.h in Headers */,
599FAA4A1FB274980058CCF6 /* RCTSurfaceView.h in Headers */,
Expand Down Expand Up @@ -2830,9 +2844,11 @@
EBF21BBD1FC498270052F4D5 /* InspectorInterfaces.cpp in Sources */,
59EDBCAF1FDF4E0C003573DE /* RCTScrollContentView.m in Sources */,
135A9C001E7B0EE600587AEB /* RCTJSCHelpers.mm in Sources */,
70347E2122FF448600A4F09B /* RCTGradient.m in Sources */,
B233E6EA1D2D845D00BC68BA /* RCTI18nManager.m in Sources */,
3D7BFD1F1EA8E351008DFB7A /* RCTPackagerConnection.mm in Sources */,
13456E931ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m in Sources */,
70347E2322FF44B700A4F09B /* RCTGradientManager.m in Sources */,
D49593DF202C937C00A7694B /* RCTCursorManager.m in Sources */,
13A1F71E1A75392D00D3D453 /* RCTKeyCommands.m in Sources */,
83CBBA531A601E3B00E9B192 /* RCTUtils.m in Sources */,
Expand Down
47 changes: 47 additions & 0 deletions React/Views/RCTGradient.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// The MIT License (MIT)
//
// Copyright (c) 2014 Jernej Strasner
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#import <React/RCTView.h>

@interface RCTGradient : RCTView

/// The starting color of the gradient.
@property (nonatomic, nonnull) NSColor *startColor;

/// The ending color of the gradient.
@property (nonatomic, nonnull) NSColor *endColor;

/// The interpolation factor of the gradient. This defines how smooth the color transition is.
@property (nonatomic) CGFloat slopeFactor;

/// If YES the gradient is drawn before the start point.
@property (nonatomic) BOOL drawsBeforeStart;

/// If YES the gradient is drawn past the end point.
@property (nonatomic) BOOL drawsAfterEnd;

/// The location of the gradient drawing start relative to the view's coordinate space (0.0 - 1.0).
@property (nonatomic) CGPoint startPoint;

/// The location of the gradient drawing end relative to the view's coordinate space (0.0 - 1.0).
@property (nonatomic) CGPoint endPoint;

@end
212 changes: 212 additions & 0 deletions React/Views/RCTGradient.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
// The MIT License (MIT)
//
// Copyright (c) 2014 Jernej Strasner
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#import "RCTGradient.h"

#import "RCTLog.h"
#import "RCTUtils.h"
#import "UIImageUtils.h"

// Info struct to pass to shading function
struct _JSTFunctionInfo {
CGFloat startColor[4];
CGFloat endColor[4];
CGFloat slopeFactor;
};
typedef struct _JSTFunctionInfo JSTFunctionInfo;
typedef struct _JSTFunctionInfo* JSTFunctionInfoRef;

static JSTFunctionInfoRef JSTFunctionInfoCreate() {
return (JSTFunctionInfoRef)malloc(sizeof(JSTFunctionInfo));
}

static void JSTFunctionInfoRelease(JSTFunctionInfoRef info) {
if (info != NULL) {
free(info);
}
}

// Distributes values on a slope aka. ease-in ease-out
static float JSTSlope(float x, float A)
{
float p = powf(x, A);
return p/(p + powf(1.0f-x, A));
}

// This is the callback of our shading function.
// info: color and slope information
// inData: contains a single float that gives is the current position within the gradient
// outData: we fill this with the color to display at the given position
static void JSTShadingFunction(void *infoPtr, const CGFloat *inData, CGFloat *outData)
{
JSTFunctionInfo info = *(JSTFunctionInfo*)infoPtr; // Info struct with colors and parameters
float p = inData[0]; // Position in gradient
float q = JSTSlope(p, info.slopeFactor); // Slope value
outData[0] = info.startColor[0] + (info.endColor[0] - info.startColor[0])*q;
outData[1] = info.startColor[1] + (info.endColor[1] - info.startColor[1])*q;
outData[2] = info.startColor[2] + (info.endColor[2] - info.startColor[2])*q;
outData[3] = info.startColor[3] + (info.endColor[3] - info.startColor[3])*q;
}

@implementation RCTGradient
{
CGColorSpaceRef _colorSpace;
CGFloat _startColorComps[4];
CGFloat _endColorComps[4];
CGFunctionRef _function;
JSTFunctionInfoRef _functionInfo;
BOOL _opaque;
}

- (instancetype)init
{
return [self initWithFrame:CGRectZero];
}

- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
_colorSpace = CGColorSpaceCreateDeviceRGB();
_startColor = [NSColor clearColor];
_endColor = [NSColor clearColor];
_startPoint = CGPointMake(0, 0.5);
_endPoint = CGPointMake(1, 0.5);
_slopeFactor = 1;
}
return self;
}

RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:unused)

- (void)dealloc
{
JSTFunctionInfoRelease(_functionInfo);
CGFunctionRelease(_function);
CGColorSpaceRelease(_colorSpace);
}

- (void)setStartColor:(NSColor *)startColor
{
if (_startColor == startColor) return;
_startColor = startColor;

[startColor getRed:_startColorComps green:_startColorComps+1 blue:_startColorComps+2 alpha:_startColorComps+3];
}

- (void)setEndColor:(NSColor *)endColor
{
if (_endColor == endColor) return;
_endColor = endColor;

[endColor getRed:_endColorComps green:_endColorComps+1 blue:_endColorComps+2 alpha:_endColorComps+3];
}

- (void)didSetProps:(NSArray<NSString *> *)changedProps
{
for (NSString *prop in changedProps) {
if ([prop isEqualToString:@"startColor"] ||
[prop isEqualToString:@"endColor"] ||
[prop isEqualToString:@"slopeFactor"]) {
[self _createShadingFunction];
break;
}
}
[self setNeedsDisplay:YES];
}

- (BOOL)isOpaque
{
return _endColorComps[3] == 1 && _startColorComps[3] == 1;
}

- (void)_createShadingFunction
{
// Shading function info
JSTFunctionInfoRelease(_functionInfo);
_functionInfo = JSTFunctionInfoCreate();
memcpy(_functionInfo->startColor, _startColorComps, sizeof(CGFloat)*4);
memcpy(_functionInfo->endColor, _endColorComps, sizeof(CGFloat)*4);
_functionInfo->slopeFactor = _slopeFactor;

// Define the shading callbacks
CGFunctionCallbacks callbacks = {0, JSTShadingFunction, NULL};

// As input to our function we want 1 value in the range [0.0, 1.0].
// This is our position within the gradient.
size_t domainDimension = 1;
CGFloat domain[2] = {0.0f, 1.0f};

// The output of our shading function are 4 values, each in the range [0.0, 1.0].
// By specifying 4 ranges here, we limit each color component to that range. Values outside of the range get clipped.
size_t rangeDimension = 4;
CGFloat range[8] = {
0.0f, 1.0f, // R
0.0f, 1.0f, // G
0.0f, 1.0f, // B
0.0f, 1.0f // A
};

// Create the shading function
CGFunctionRelease(_function);
_function = CGFunctionCreate(_functionInfo, domainDimension, domain, rangeDimension, range, &callbacks);
}

- (void)displayLayer:(CALayer *)layer
{
[super displayLayer:layer];

const CGSize size = self.bounds.size;
UIGraphicsBeginImageContextWithOptions(size, self.isOpaque, 0.0);

// Preserve contents from super call.
NSImage *contents = layer.contents;
[contents drawInRect:self.bounds];

// Draw the gradient.
[self drawRect:self.bounds];

// Fetch the drawing bitmap.
contents = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

layer.contents = contents;
layer.magnificationFilter = kCAFilterNearest;
layer.needsDisplayOnBoundsChange = YES;
}

- (void)drawRect:(CGRect)rect
{
// Prepare general variables
CGContextRef context = UIGraphicsGetCurrentContext();

// Create the shading object
CGPoint startPoint = CGPointMake(_startPoint.x * rect.size.width, _startPoint.y * rect.size.height);
CGPoint endPoint = CGPointMake(_endPoint.x * rect.size.width, _endPoint.y * rect.size.height);
CGShadingRef shading = CGShadingCreateAxial(_colorSpace, startPoint, endPoint, _function, _drawsBeforeStart, _drawsAfterEnd);

// Draw the shading
CGContextDrawShading(context, shading);

// Clean up
CGShadingRelease(shading);
}

@end
Loading

0 comments on commit 1f82867

Please sign in to comment.