react-native-linear-gradient-fabric 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/lineargradientfabric/LinearGradientView.kt +1 -1
- package/ios/LinearGradientView.m +7 -0
- package/ios/LinearGradientViewComponentView.mm +7 -0
- package/lib/commonjs/LinearGradientNativeComponent.js.map +1 -1
- package/lib/commonjs/index.js +89 -5
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/LinearGradientNativeComponent.js.map +1 -1
- package/lib/module/index.js +89 -5
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/LinearGradientNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -10
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/LinearGradientNativeComponent.ts +1 -1
- package/src/index.tsx +96 -15
|
@@ -67,7 +67,7 @@ class LinearGradientView(context: Context) : FrameLayout(context) {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
override fun onDraw(canvas: Canvas) {
|
|
70
|
-
if (colors.size < 2 || width
|
|
70
|
+
if (colors.size < 2 || width <= 0 || height <= 0) {
|
|
71
71
|
super.onDraw(canvas)
|
|
72
72
|
return
|
|
73
73
|
}
|
package/ios/LinearGradientView.m
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
if (self = [super initWithFrame:frame]) {
|
|
11
11
|
_gradientLayer = [CAGradientLayer layer];
|
|
12
12
|
_gradientLayer.frame = self.bounds;
|
|
13
|
+
_gradientLayer.zPosition = -1000;
|
|
13
14
|
[self.layer insertSublayer:_gradientLayer atIndex:0];
|
|
14
15
|
|
|
15
16
|
// Set default values
|
|
@@ -26,6 +27,7 @@
|
|
|
26
27
|
{
|
|
27
28
|
[super layoutSubviews];
|
|
28
29
|
_gradientLayer.frame = self.bounds;
|
|
30
|
+
[self updateGradient];
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
- (void)setColors:(NSArray<NSNumber *> *)colors
|
|
@@ -72,6 +74,11 @@
|
|
|
72
74
|
|
|
73
75
|
- (void)updateGradient
|
|
74
76
|
{
|
|
77
|
+
// Guard against zero-dimension bounds (iOS 17+ crash fix, see issue #652)
|
|
78
|
+
if (self.bounds.size.width <= 0 || self.bounds.size.height <= 0) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
75
82
|
if (!_colors || _colors.count == 0) {
|
|
76
83
|
return;
|
|
77
84
|
}
|
|
@@ -40,6 +40,7 @@ using namespace facebook::react;
|
|
|
40
40
|
|
|
41
41
|
_gradientLayer = [CAGradientLayer layer];
|
|
42
42
|
_gradientLayer.frame = self.bounds;
|
|
43
|
+
_gradientLayer.zPosition = -1000;
|
|
43
44
|
[self.layer insertSublayer:_gradientLayer atIndex:0];
|
|
44
45
|
|
|
45
46
|
// Set default values
|
|
@@ -59,6 +60,7 @@ using namespace facebook::react;
|
|
|
59
60
|
{
|
|
60
61
|
[super layoutSubviews];
|
|
61
62
|
_gradientLayer.frame = self.bounds;
|
|
63
|
+
[self updateGradient];
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
|
@@ -98,6 +100,11 @@ using namespace facebook::react;
|
|
|
98
100
|
|
|
99
101
|
- (void)updateGradient
|
|
100
102
|
{
|
|
103
|
+
// Guard against zero-dimension bounds (iOS 17+ crash fix, see issue #652)
|
|
104
|
+
if (self.bounds.size.width <= 0 || self.bounds.size.height <= 0) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
101
108
|
if (_colors.empty()) {
|
|
102
109
|
return;
|
|
103
110
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_codegenNativeComponent","_interopRequireDefault","require","e","__esModule","default","_default","exports","codegenNativeComponent"],"sourceRoot":"../../src","sources":["LinearGradientNativeComponent.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"names":["_codegenNativeComponent","_interopRequireDefault","require","e","__esModule","default","_default","exports","codegenNativeComponent"],"sourceRoot":"../../src","sources":["LinearGradientNativeComponent.ts"],"mappings":";;;;;;AAEA,IAAAA,uBAAA,GAAAC,sBAAA,CAAAC,OAAA;AAA6F,SAAAD,uBAAAE,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAAA,IAAAG,QAAA,GAAAC,OAAA,CAAAF,OAAA,GAsC9E,IAAAG,+BAAsB,EACnC,oBACF,CAAC","ignoreList":[]}
|
package/lib/commonjs/index.js
CHANGED
|
@@ -23,14 +23,96 @@ const DEFAULT_ANGLE_CENTER = {
|
|
|
23
23
|
x: 0.5,
|
|
24
24
|
y: 0.5
|
|
25
25
|
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Check if a processed color has zero alpha (fully transparent).
|
|
29
|
+
* React Native processColor returns ARGB format where alpha is in the highest byte.
|
|
30
|
+
*/
|
|
31
|
+
function isTransparentColor(colorInt) {
|
|
32
|
+
// Extract alpha from ARGB format (bits 24-31)
|
|
33
|
+
const alpha = colorInt >>> 24 & 0xff;
|
|
34
|
+
return alpha === 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get the RGB components from a processed color (ARGB format).
|
|
39
|
+
*/
|
|
40
|
+
function getRGB(colorInt) {
|
|
41
|
+
return {
|
|
42
|
+
r: colorInt >>> 16 & 0xff,
|
|
43
|
+
g: colorInt >>> 8 & 0xff,
|
|
44
|
+
b: colorInt & 0xff
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Create a color with specified RGB and alpha=0 (fully transparent).
|
|
50
|
+
*/
|
|
51
|
+
function createTransparentColor(r, g, b) {
|
|
52
|
+
// ARGB format: alpha=0 in highest byte
|
|
53
|
+
return (0 << 24 | r << 16 | g << 8 | b) >>> 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Fix transparent colors in gradients to avoid grey interpolation artifacts.
|
|
58
|
+
*
|
|
59
|
+
* When interpolating from 'transparent' (rgba(0,0,0,0)) to a color like white,
|
|
60
|
+
* the gradient interpolates all RGBA channels, resulting in grey midtones.
|
|
61
|
+
* This function replaces fully transparent colors with transparent versions
|
|
62
|
+
* of their nearest opaque neighbor, ensuring smooth color transitions.
|
|
63
|
+
*
|
|
64
|
+
* See: https://github.com/react-native-linear-gradient/react-native-linear-gradient/issues/691
|
|
65
|
+
*/
|
|
66
|
+
function fixTransparentColors(colors) {
|
|
67
|
+
const result = [...colors];
|
|
68
|
+
for (let i = 0; i < result.length; i++) {
|
|
69
|
+
const currentColor = result[i];
|
|
70
|
+
if (currentColor !== undefined && isTransparentColor(currentColor)) {
|
|
71
|
+
// Find nearest non-transparent color to inherit RGB from
|
|
72
|
+
let nearestOpaqueIdx = -1;
|
|
73
|
+
let minDistance = Infinity;
|
|
74
|
+
for (let j = 0; j < result.length; j++) {
|
|
75
|
+
const candidateColor = result[j];
|
|
76
|
+
if (i !== j && candidateColor !== undefined && !isTransparentColor(candidateColor)) {
|
|
77
|
+
const distance = Math.abs(i - j);
|
|
78
|
+
if (distance < minDistance) {
|
|
79
|
+
minDistance = distance;
|
|
80
|
+
nearestOpaqueIdx = j;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// If we found a non-transparent color, use its RGB values
|
|
86
|
+
const nearestColor = nearestOpaqueIdx !== -1 ? result[nearestOpaqueIdx] : undefined;
|
|
87
|
+
if (nearestColor !== undefined) {
|
|
88
|
+
const {
|
|
89
|
+
r,
|
|
90
|
+
g,
|
|
91
|
+
b
|
|
92
|
+
} = getRGB(nearestColor);
|
|
93
|
+
result[i] = createTransparentColor(r, g, b);
|
|
94
|
+
}
|
|
95
|
+
// If all colors are transparent, leave as-is (nothing to fix)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
26
100
|
function processColors(colors) {
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
if (
|
|
101
|
+
const processed = colors.map(color => {
|
|
102
|
+
const result = (0, _reactNative.processColor)(color);
|
|
103
|
+
if (result === null || result === undefined) {
|
|
30
104
|
throw new Error(`Invalid color value: ${String(color)}`);
|
|
31
105
|
}
|
|
32
|
-
|
|
106
|
+
if (typeof result === 'number') {
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
// OpaqueColorValue (symbol type) or other non-number values
|
|
110
|
+
// This can happen with platform-specific color types
|
|
111
|
+
throw new Error(`Unsupported color type: ${String(color)}. Only standard color values are supported.`);
|
|
33
112
|
});
|
|
113
|
+
|
|
114
|
+
// Fix transparent colors to prevent grey gradient artifacts
|
|
115
|
+
return fixTransparentColors(processed);
|
|
34
116
|
}
|
|
35
117
|
function validateLocations(locations, colorsLength) {
|
|
36
118
|
if (!locations) {
|
|
@@ -39,7 +121,9 @@ function validateLocations(locations, colorsLength) {
|
|
|
39
121
|
if (locations.length !== colorsLength) {
|
|
40
122
|
console.warn(`LinearGradient: locations array length (${locations.length}) does not match colors array length (${colorsLength})`);
|
|
41
123
|
}
|
|
42
|
-
|
|
124
|
+
|
|
125
|
+
// Clamp location values to [0, 1] range for iOS CAGradientLayer compatibility
|
|
126
|
+
return locations.map(value => Math.max(0, Math.min(1, value)));
|
|
43
127
|
}
|
|
44
128
|
function LinearGradient({
|
|
45
129
|
colors,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","_interopRequireWildcard","require","_reactNative","_LinearGradientNativeComponent","_interopRequireDefault","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","_extends","assign","bind","arguments","length","apply","DEFAULT_START","x","y","DEFAULT_END","DEFAULT_ANGLE_CENTER","
|
|
1
|
+
{"version":3,"names":["React","_interopRequireWildcard","require","_reactNative","_LinearGradientNativeComponent","_interopRequireDefault","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","_extends","assign","bind","arguments","length","apply","DEFAULT_START","x","y","DEFAULT_END","DEFAULT_ANGLE_CENTER","isTransparentColor","colorInt","alpha","getRGB","g","b","createTransparentColor","fixTransparentColors","colors","result","currentColor","undefined","nearestOpaqueIdx","minDistance","Infinity","j","candidateColor","distance","Math","abs","nearestColor","processColors","processed","map","color","processColor","Error","String","validateLocations","locations","colorsLength","console","warn","value","max","min","LinearGradient","start","end","useAngle","angle","angleCenter","style","children","rest","processedColors","useMemo","validatedLocations","createElement","StyleSheet","flatten","startPoint","endPoint","_default","exports"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["codegenNativeComponent"],"sourceRoot":"../../src","sources":["LinearGradientNativeComponent.ts"],"mappings":"
|
|
1
|
+
{"version":3,"names":["codegenNativeComponent"],"sourceRoot":"../../src","sources":["LinearGradientNativeComponent.ts"],"mappings":"AAEA,OAAOA,sBAAsB,MAAM,yDAAyD;AAsC5F,eAAeA,sBAAsB,CACnC,oBACF,CAAC","ignoreList":[]}
|
package/lib/module/index.js
CHANGED
|
@@ -14,14 +14,96 @@ const DEFAULT_ANGLE_CENTER = {
|
|
|
14
14
|
x: 0.5,
|
|
15
15
|
y: 0.5
|
|
16
16
|
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Check if a processed color has zero alpha (fully transparent).
|
|
20
|
+
* React Native processColor returns ARGB format where alpha is in the highest byte.
|
|
21
|
+
*/
|
|
22
|
+
function isTransparentColor(colorInt) {
|
|
23
|
+
// Extract alpha from ARGB format (bits 24-31)
|
|
24
|
+
const alpha = colorInt >>> 24 & 0xff;
|
|
25
|
+
return alpha === 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get the RGB components from a processed color (ARGB format).
|
|
30
|
+
*/
|
|
31
|
+
function getRGB(colorInt) {
|
|
32
|
+
return {
|
|
33
|
+
r: colorInt >>> 16 & 0xff,
|
|
34
|
+
g: colorInt >>> 8 & 0xff,
|
|
35
|
+
b: colorInt & 0xff
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Create a color with specified RGB and alpha=0 (fully transparent).
|
|
41
|
+
*/
|
|
42
|
+
function createTransparentColor(r, g, b) {
|
|
43
|
+
// ARGB format: alpha=0 in highest byte
|
|
44
|
+
return (0 << 24 | r << 16 | g << 8 | b) >>> 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Fix transparent colors in gradients to avoid grey interpolation artifacts.
|
|
49
|
+
*
|
|
50
|
+
* When interpolating from 'transparent' (rgba(0,0,0,0)) to a color like white,
|
|
51
|
+
* the gradient interpolates all RGBA channels, resulting in grey midtones.
|
|
52
|
+
* This function replaces fully transparent colors with transparent versions
|
|
53
|
+
* of their nearest opaque neighbor, ensuring smooth color transitions.
|
|
54
|
+
*
|
|
55
|
+
* See: https://github.com/react-native-linear-gradient/react-native-linear-gradient/issues/691
|
|
56
|
+
*/
|
|
57
|
+
function fixTransparentColors(colors) {
|
|
58
|
+
const result = [...colors];
|
|
59
|
+
for (let i = 0; i < result.length; i++) {
|
|
60
|
+
const currentColor = result[i];
|
|
61
|
+
if (currentColor !== undefined && isTransparentColor(currentColor)) {
|
|
62
|
+
// Find nearest non-transparent color to inherit RGB from
|
|
63
|
+
let nearestOpaqueIdx = -1;
|
|
64
|
+
let minDistance = Infinity;
|
|
65
|
+
for (let j = 0; j < result.length; j++) {
|
|
66
|
+
const candidateColor = result[j];
|
|
67
|
+
if (i !== j && candidateColor !== undefined && !isTransparentColor(candidateColor)) {
|
|
68
|
+
const distance = Math.abs(i - j);
|
|
69
|
+
if (distance < minDistance) {
|
|
70
|
+
minDistance = distance;
|
|
71
|
+
nearestOpaqueIdx = j;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// If we found a non-transparent color, use its RGB values
|
|
77
|
+
const nearestColor = nearestOpaqueIdx !== -1 ? result[nearestOpaqueIdx] : undefined;
|
|
78
|
+
if (nearestColor !== undefined) {
|
|
79
|
+
const {
|
|
80
|
+
r,
|
|
81
|
+
g,
|
|
82
|
+
b
|
|
83
|
+
} = getRGB(nearestColor);
|
|
84
|
+
result[i] = createTransparentColor(r, g, b);
|
|
85
|
+
}
|
|
86
|
+
// If all colors are transparent, leave as-is (nothing to fix)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
17
91
|
function processColors(colors) {
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
if (
|
|
92
|
+
const processed = colors.map(color => {
|
|
93
|
+
const result = processColor(color);
|
|
94
|
+
if (result === null || result === undefined) {
|
|
21
95
|
throw new Error(`Invalid color value: ${String(color)}`);
|
|
22
96
|
}
|
|
23
|
-
|
|
97
|
+
if (typeof result === 'number') {
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
// OpaqueColorValue (symbol type) or other non-number values
|
|
101
|
+
// This can happen with platform-specific color types
|
|
102
|
+
throw new Error(`Unsupported color type: ${String(color)}. Only standard color values are supported.`);
|
|
24
103
|
});
|
|
104
|
+
|
|
105
|
+
// Fix transparent colors to prevent grey gradient artifacts
|
|
106
|
+
return fixTransparentColors(processed);
|
|
25
107
|
}
|
|
26
108
|
function validateLocations(locations, colorsLength) {
|
|
27
109
|
if (!locations) {
|
|
@@ -30,7 +112,9 @@ function validateLocations(locations, colorsLength) {
|
|
|
30
112
|
if (locations.length !== colorsLength) {
|
|
31
113
|
console.warn(`LinearGradient: locations array length (${locations.length}) does not match colors array length (${colorsLength})`);
|
|
32
114
|
}
|
|
33
|
-
|
|
115
|
+
|
|
116
|
+
// Clamp location values to [0, 1] range for iOS CAGradientLayer compatibility
|
|
117
|
+
return locations.map(value => Math.max(0, Math.min(1, value)));
|
|
34
118
|
}
|
|
35
119
|
export function LinearGradient({
|
|
36
120
|
colors,
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","processColor","StyleSheet","LinearGradientNativeComponent","DEFAULT_START","x","y","DEFAULT_END","DEFAULT_ANGLE_CENTER","
|
|
1
|
+
{"version":3,"names":["React","processColor","StyleSheet","LinearGradientNativeComponent","DEFAULT_START","x","y","DEFAULT_END","DEFAULT_ANGLE_CENTER","isTransparentColor","colorInt","alpha","getRGB","r","g","b","createTransparentColor","fixTransparentColors","colors","result","i","length","currentColor","undefined","nearestOpaqueIdx","minDistance","Infinity","j","candidateColor","distance","Math","abs","nearestColor","processColors","processed","map","color","Error","String","validateLocations","locations","colorsLength","console","warn","value","max","min","LinearGradient","start","end","useAngle","angle","angleCenter","style","children","rest","processedColors","useMemo","validatedLocations","createElement","_extends","flatten","startPoint","endPoint"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LinearGradientNativeComponent.d.ts","sourceRoot":"","sources":["../../../src/LinearGradientNativeComponent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"LinearGradientNativeComponent.d.ts","sourceRoot":"","sources":["../../../src/LinearGradientNativeComponent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,2CAA2C,CAAC;AAG9E,MAAM,WAAW,yBAAyB;IACxC,CAAC,EAAE,KAAK,CAAC;IACT,CAAC,EAAE,KAAK,CAAC;CACV;AAED,MAAM,WAAW,yBAA0B,SAAQ,SAAS;IAC1D;;OAEG;IACH,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC;IAC7B;;OAEG;IACH,SAAS,CAAC,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC;IACjC;;OAEG;IACH,UAAU,CAAC,EAAE,yBAAyB,CAAC;IACvC;;OAEG;IACH,QAAQ,CAAC,EAAE,yBAAyB,CAAC;IACrC;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC;IACd;;OAEG;IACH,WAAW,CAAC,EAAE,yBAAyB,CAAC;CACzC;;AAED,wBAEE"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import type { ColorValue,
|
|
2
|
+
import type { ColorValue, ViewProps } from 'react-native';
|
|
3
3
|
export interface Point {
|
|
4
4
|
x: number;
|
|
5
5
|
y: number;
|
|
6
6
|
}
|
|
7
|
-
export interface LinearGradientProps {
|
|
7
|
+
export interface LinearGradientProps extends ViewProps {
|
|
8
8
|
/**
|
|
9
9
|
* An array of at least 2 colors that represent gradient colors
|
|
10
10
|
*/
|
|
@@ -40,14 +40,6 @@ export interface LinearGradientProps {
|
|
|
40
40
|
* Default: { x: 0.5, y: 0.5 }
|
|
41
41
|
*/
|
|
42
42
|
angleCenter?: Point;
|
|
43
|
-
/**
|
|
44
|
-
* Style for the gradient view
|
|
45
|
-
*/
|
|
46
|
-
style?: StyleProp<ViewStyle>;
|
|
47
|
-
/**
|
|
48
|
-
* Children elements to render on top of the gradient
|
|
49
|
-
*/
|
|
50
|
-
children?: React.ReactNode;
|
|
51
43
|
}
|
|
52
44
|
export declare function LinearGradient({ colors, locations, start, end, useAngle, angle, angleCenter, style, children, ...rest }: LinearGradientProps): React.ReactElement;
|
|
53
45
|
export default LinearGradient;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAK1D,MAAM,WAAW,KAAK;IACpB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,mBAAoB,SAAQ,SAAS;IACpD;;OAEG;IACH,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB;;;OAGG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC;IACd;;;OAGG;IACH,GAAG,CAAC,EAAE,KAAK,CAAC;IACZ;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,WAAW,CAAC,EAAE,KAAK,CAAC;CACrB;AA0HD,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,SAAS,EACT,KAAqB,EACrB,GAAiB,EACjB,QAAgB,EAChB,KAAS,EACT,WAAkC,EAClC,KAAK,EACL,QAAQ,EACR,GAAG,IAAI,EACR,EAAE,mBAAmB,GAAG,KAAK,CAAC,YAAY,CA0B1C;AAED,eAAe,cAAc,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ViewProps } from 'react-native';
|
|
2
|
-
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
3
2
|
import type { Float, Int32 } from 'react-native/Libraries/Types/CodegenTypes';
|
|
3
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
4
4
|
|
|
5
5
|
export interface NativeLinearGradientPoint {
|
|
6
6
|
x: Float;
|
package/src/index.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import type { ColorValue,
|
|
2
|
+
import type { ColorValue, ViewProps } from 'react-native';
|
|
3
3
|
import { processColor, StyleSheet } from 'react-native';
|
|
4
4
|
|
|
5
5
|
import LinearGradientNativeComponent from './LinearGradientNativeComponent';
|
|
@@ -9,7 +9,7 @@ export interface Point {
|
|
|
9
9
|
y: number;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export interface LinearGradientProps {
|
|
12
|
+
export interface LinearGradientProps extends ViewProps {
|
|
13
13
|
/**
|
|
14
14
|
* An array of at least 2 colors that represent gradient colors
|
|
15
15
|
*/
|
|
@@ -45,28 +45,108 @@ export interface LinearGradientProps {
|
|
|
45
45
|
* Default: { x: 0.5, y: 0.5 }
|
|
46
46
|
*/
|
|
47
47
|
angleCenter?: Point;
|
|
48
|
-
/**
|
|
49
|
-
* Style for the gradient view
|
|
50
|
-
*/
|
|
51
|
-
style?: StyleProp<ViewStyle>;
|
|
52
|
-
/**
|
|
53
|
-
* Children elements to render on top of the gradient
|
|
54
|
-
*/
|
|
55
|
-
children?: React.ReactNode;
|
|
56
48
|
}
|
|
57
49
|
|
|
58
50
|
const DEFAULT_START: Point = { x: 0.5, y: 0 };
|
|
59
51
|
const DEFAULT_END: Point = { x: 0.5, y: 1 };
|
|
60
52
|
const DEFAULT_ANGLE_CENTER: Point = { x: 0.5, y: 0.5 };
|
|
61
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Check if a processed color has zero alpha (fully transparent).
|
|
56
|
+
* React Native processColor returns ARGB format where alpha is in the highest byte.
|
|
57
|
+
*/
|
|
58
|
+
function isTransparentColor(colorInt: number): boolean {
|
|
59
|
+
// Extract alpha from ARGB format (bits 24-31)
|
|
60
|
+
const alpha = (colorInt >>> 24) & 0xff;
|
|
61
|
+
return alpha === 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get the RGB components from a processed color (ARGB format).
|
|
66
|
+
*/
|
|
67
|
+
function getRGB(colorInt: number): { r: number; g: number; b: number } {
|
|
68
|
+
return {
|
|
69
|
+
r: (colorInt >>> 16) & 0xff,
|
|
70
|
+
g: (colorInt >>> 8) & 0xff,
|
|
71
|
+
b: colorInt & 0xff,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Create a color with specified RGB and alpha=0 (fully transparent).
|
|
77
|
+
*/
|
|
78
|
+
function createTransparentColor(r: number, g: number, b: number): number {
|
|
79
|
+
// ARGB format: alpha=0 in highest byte
|
|
80
|
+
return ((0 << 24) | (r << 16) | (g << 8) | b) >>> 0;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Fix transparent colors in gradients to avoid grey interpolation artifacts.
|
|
85
|
+
*
|
|
86
|
+
* When interpolating from 'transparent' (rgba(0,0,0,0)) to a color like white,
|
|
87
|
+
* the gradient interpolates all RGBA channels, resulting in grey midtones.
|
|
88
|
+
* This function replaces fully transparent colors with transparent versions
|
|
89
|
+
* of their nearest opaque neighbor, ensuring smooth color transitions.
|
|
90
|
+
*
|
|
91
|
+
* See: https://github.com/react-native-linear-gradient/react-native-linear-gradient/issues/691
|
|
92
|
+
*/
|
|
93
|
+
function fixTransparentColors(colors: number[]): number[] {
|
|
94
|
+
const result = [...colors];
|
|
95
|
+
|
|
96
|
+
for (let i = 0; i < result.length; i++) {
|
|
97
|
+
const currentColor = result[i];
|
|
98
|
+
if (currentColor !== undefined && isTransparentColor(currentColor)) {
|
|
99
|
+
// Find nearest non-transparent color to inherit RGB from
|
|
100
|
+
let nearestOpaqueIdx = -1;
|
|
101
|
+
let minDistance = Infinity;
|
|
102
|
+
|
|
103
|
+
for (let j = 0; j < result.length; j++) {
|
|
104
|
+
const candidateColor = result[j];
|
|
105
|
+
if (
|
|
106
|
+
i !== j &&
|
|
107
|
+
candidateColor !== undefined &&
|
|
108
|
+
!isTransparentColor(candidateColor)
|
|
109
|
+
) {
|
|
110
|
+
const distance = Math.abs(i - j);
|
|
111
|
+
if (distance < minDistance) {
|
|
112
|
+
minDistance = distance;
|
|
113
|
+
nearestOpaqueIdx = j;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// If we found a non-transparent color, use its RGB values
|
|
119
|
+
const nearestColor =
|
|
120
|
+
nearestOpaqueIdx !== -1 ? result[nearestOpaqueIdx] : undefined;
|
|
121
|
+
if (nearestColor !== undefined) {
|
|
122
|
+
const { r, g, b } = getRGB(nearestColor);
|
|
123
|
+
result[i] = createTransparentColor(r, g, b);
|
|
124
|
+
}
|
|
125
|
+
// If all colors are transparent, leave as-is (nothing to fix)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
|
|
62
132
|
function processColors(colors: ColorValue[]): number[] {
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
if (
|
|
133
|
+
const processed = colors.map((color) => {
|
|
134
|
+
const result = processColor(color);
|
|
135
|
+
if (result === null || result === undefined) {
|
|
66
136
|
throw new Error(`Invalid color value: ${String(color)}`);
|
|
67
137
|
}
|
|
68
|
-
|
|
138
|
+
if (typeof result === 'number') {
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
// OpaqueColorValue (symbol type) or other non-number values
|
|
142
|
+
// This can happen with platform-specific color types
|
|
143
|
+
throw new Error(
|
|
144
|
+
`Unsupported color type: ${String(color)}. Only standard color values are supported.`
|
|
145
|
+
);
|
|
69
146
|
});
|
|
147
|
+
|
|
148
|
+
// Fix transparent colors to prevent grey gradient artifacts
|
|
149
|
+
return fixTransparentColors(processed);
|
|
70
150
|
}
|
|
71
151
|
|
|
72
152
|
function validateLocations(
|
|
@@ -83,7 +163,8 @@ function validateLocations(
|
|
|
83
163
|
);
|
|
84
164
|
}
|
|
85
165
|
|
|
86
|
-
|
|
166
|
+
// Clamp location values to [0, 1] range for iOS CAGradientLayer compatibility
|
|
167
|
+
return locations.map((value) => Math.max(0, Math.min(1, value)));
|
|
87
168
|
}
|
|
88
169
|
|
|
89
170
|
export function LinearGradient({
|