UNPKG

3.22 kBTypeScriptView Raw
1import * as React from 'react';
2import { LayoutRectangle, View } from 'react-native';
3import normalizeColor from 'react-native-web/src/modules/normalizeColor';
4
5import { NativeLinearGradientPoint, NativeLinearGradientProps } from './NativeLinearGradient.types';
6
7export 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 // Convert 0...1 to 0...100
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 // TODO(Bacon): In the future we could consider adding `backgroundRepeat: "no-repeat"`. For more
68 // browser support.
69 return (
70 <View
71 {...props}
72 style={[
73 props.style,
74 // @ts-ignore: [ts] Property 'backgroundImage' does not exist on type 'ViewStyle'.
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 // don't set new layout state unless the layout has actually changed
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}