1 | import * as React from 'react';
|
2 | import { LayoutRectangle, View } from 'react-native';
|
3 | import normalizeColor from 'react-native-web/src/modules/normalizeColor';
|
4 |
|
5 | import { NativeLinearGradientPoint, NativeLinearGradientProps } from './NativeLinearGradient.types';
|
6 |
|
7 | export default function NativeLinearGradient({
|
8 | colors,
|
9 | locations,
|
10 | startPoint,
|
11 | endPoint,
|
12 | ...props
|
13 | }: NativeLinearGradientProps): React.ReactElement {
|
14 | const [layout, setLayout] = React.useState<LayoutRectangle | null>(null);
|
15 | const [gradientColors, setGradientColors] = React.useState<string[]>([]);
|
16 | const [pseudoAngle, setPseudoAngle] = React.useState<number>(0);
|
17 |
|
18 | const { width = 1, height = 1 } = layout ?? {};
|
19 | React.useEffect(() => {
|
20 | const getControlPoints = (): NativeLinearGradientPoint[] => {
|
21 | let correctedStartPoint: NativeLinearGradientPoint = [0, 0];
|
22 | if (Array.isArray(startPoint)) {
|
23 | correctedStartPoint = [
|
24 | startPoint[0] != null ? startPoint[0] : 0.0,
|
25 | startPoint[1] != null ? startPoint[1] : 0.0,
|
26 | ];
|
27 | }
|
28 | let correctedEndPoint: NativeLinearGradientPoint = [0.0, 1.0];
|
29 | if (Array.isArray(endPoint)) {
|
30 | correctedEndPoint = [
|
31 | endPoint[0] != null ? endPoint[0] : 0.0,
|
32 | endPoint[1] != null ? endPoint[1] : 1.0,
|
33 | ];
|
34 | }
|
35 | return [correctedStartPoint, correctedEndPoint];
|
36 | };
|
37 |
|
38 | const [start, end] = getControlPoints();
|
39 | start[0] *= width;
|
40 | end[0] *= width;
|
41 | start[1] *= height;
|
42 | end[1] *= height;
|
43 | const py = end[1] - start[1];
|
44 | const px = end[0] - start[0];
|
45 |
|
46 | setPseudoAngle(90 + (Math.atan2(py, px) * 180) / Math.PI);
|
47 | }, [width, height, startPoint, endPoint]);
|
48 |
|
49 | React.useEffect(() => {
|
50 | const nextGradientColors = colors.map((color: number, index: number): string => {
|
51 | const hexColor = normalizeColor(color);
|
52 | let output = hexColor;
|
53 | if (locations && locations[index]) {
|
54 | const location = Math.max(0, Math.min(1, locations[index]));
|
55 |
|
56 | const percentage = location * 100;
|
57 | output += ` ${percentage}%`;
|
58 | }
|
59 | return output;
|
60 | });
|
61 |
|
62 | setGradientColors(nextGradientColors);
|
63 | }, [colors, locations]);
|
64 |
|
65 | const colorStyle = gradientColors.join(',');
|
66 | const backgroundImage = `linear-gradient(${pseudoAngle}deg, ${colorStyle})`;
|
67 |
|
68 |
|
69 | return (
|
70 | <View
|
71 | {...props}
|
72 | style={[
|
73 | props.style,
|
74 |
|
75 | { backgroundImage },
|
76 | ]}
|
77 | onLayout={event => {
|
78 | const { x, y, width, height } = event.nativeEvent.layout;
|
79 | const oldLayout = layout ?? { x: 0, y: 0, width: 1, height: 1 };
|
80 |
|
81 | if (
|
82 | x !== oldLayout.x ||
|
83 | y !== oldLayout.y ||
|
84 | width !== oldLayout.width ||
|
85 | height !== oldLayout.height
|
86 | ) {
|
87 | setLayout({ x, y, width, height });
|
88 | }
|
89 |
|
90 | if (props.onLayout) {
|
91 | props.onLayout(event);
|
92 | }
|
93 | }}
|
94 | />
|
95 | );
|
96 | }
|