UNPKG

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