1import { LinearGradient } from 'expo-linear-gradient'; 2import React from 'react'; 3import { Image, Platform, Animated, ScrollView, StyleSheet, Text, View } from 'react-native'; 4 5import MonoText from '../components/MonoText'; 6 7// https://github.com/expo/expo/issues/10599 8const AnimatedLinearGradient = Animated.createAnimatedComponent(LinearGradient); 9 10function incrementColor(color: string, step: number) { 11 const intColor = parseInt(color.substr(1), 16); 12 const newIntColor = (intColor + step).toString(16); 13 return `#${'0'.repeat(6 - newIntColor.length)}${newIntColor}`; 14} 15 16type State = { 17 count: number; 18 colorTop: string; 19 colorBottom: string; 20}; 21 22// See: https://github.com/expo/expo/pull/10229#discussion_r490961694 23// eslint-disable-next-line @typescript-eslint/ban-types 24export default class LinearGradientScreen extends React.Component<{}, State> { 25 static navigationOptions = { 26 title: 'LinearGradient', 27 }; 28 29 state = { 30 count: 0, 31 colorTop: '#000000', 32 colorBottom: '#cccccc', 33 }; 34 35 _interval?: number; 36 37 componentDidMount() { 38 // @ts-expect-error: TS resolves node types first 39 this._interval = setInterval(() => { 40 this.setState((state) => ({ 41 count: state.count + 1, 42 colorTop: incrementColor(state.colorTop, 1), 43 colorBottom: incrementColor(state.colorBottom, -1), 44 })); 45 }, 100); 46 } 47 48 componentWillUnmount() { 49 clearInterval(this._interval); 50 } 51 52 render() { 53 const location = Math.sin(this.state.count / 100) * 0.5; 54 const position = Math.sin(this.state.count / 100); 55 return ( 56 <ScrollView 57 style={{ flex: 1 }} 58 contentContainerStyle={{ 59 alignItems: 'stretch', 60 paddingVertical: 10, 61 }}> 62 <AnimatedLinearGradient 63 style={{ display: 'none' }} 64 colors={[this.state.colorTop, this.state.colorBottom]} 65 /> 66 <ColorsTest colors={[this.state.colorTop, this.state.colorBottom]} /> 67 <LocationsTest locations={[location, 1.0 - location]} /> 68 <ControlPointTest start={[position, 0]} /> 69 {Platform.OS !== 'web' && <SnapshotTest />} 70 </ScrollView> 71 ); 72 } 73} 74 75const Container: React.FunctionComponent<{ title: string; children?: React.ReactNode }> = ({ 76 title, 77 children, 78}) => ( 79 <View style={styles.container}> 80 <Text style={styles.containerTitle}>{title}</Text> 81 {children} 82 </View> 83); 84 85const SnapshotTest = () => ( 86 <Container title="Snapshot"> 87 <View style={{ flexDirection: 'row', alignSelf: 'stretch', justifyContent: 'space-evenly' }}> 88 <LinearGradient 89 colors={['white', 'red']} 90 start={[0.5, 0.5]} 91 end={[1, 1]} 92 style={{ 93 width: 100, 94 maxHeight: 200, 95 minHeight: 200, 96 borderWidth: 1, 97 marginVertical: 20, 98 borderColor: 'black', 99 }} 100 /> 101 <Image 102 source={require('../../assets/images/confusing_gradient.png')} 103 style={{ width: 100, height: 200, marginVertical: 20 }} 104 /> 105 </View> 106 <Text style={{ marginHorizontal: 20 }}>The gradients above should look the same.</Text> 107 </Container> 108); 109 110const ControlPointTest: React.FunctionComponent<{ 111 start?: [number, number]; 112 end?: [number, number]; 113}> = ({ start = [0.5, 0], end = [0, 1] }) => { 114 const startInfo = `start={[${start.map((point) => +point.toFixed(2)).join(', ')}]}`; 115 const endInfo = `end={[${end.map((point) => +point.toFixed(2)).join(', ')}]}`; 116 117 return ( 118 <Container title="Control Points"> 119 <View> 120 {[startInfo, endInfo].map((pointInfo, index) => ( 121 <MonoText key={'--' + index}>{pointInfo}</MonoText> 122 ))} 123 </View> 124 <LinearGradient 125 start={start} 126 end={end} 127 locations={[0.5, 0.5]} 128 colors={['blue', 'lime']} 129 style={styles.gradient} 130 /> 131 </Container> 132 ); 133}; 134 135const ColorsTest = ({ colors }: { colors: string[] }) => { 136 const info = colors.map((value) => `"${value}"`).join(', '); 137 return ( 138 <Container title="Colors"> 139 <MonoText>{`colors={[${info}]}`}</MonoText> 140 <LinearGradient colors={colors} style={styles.gradient} /> 141 </Container> 142 ); 143}; 144 145const LocationsTest: React.FunctionComponent<{ locations: number[] }> = ({ locations }) => { 146 const locationsInfo = locations.map((location) => +location.toFixed(2)).join(', '); 147 return ( 148 <Container title="Locations"> 149 <MonoText>{`locations={[${locationsInfo}]}`}</MonoText> 150 <LinearGradient colors={['red', 'blue']} locations={locations} style={styles.gradient} /> 151 </Container> 152 ); 153}; 154 155const styles = StyleSheet.create({ 156 container: { 157 padding: 8, 158 flexShrink: 0, 159 }, 160 containerTitle: { 161 fontSize: 14, 162 fontWeight: '600', 163 textAlign: 'center', 164 marginBottom: 8, 165 }, 166 gradient: { 167 flex: 1, 168 flexShrink: 0, 169 minHeight: 200, 170 maxHeight: 200, 171 }, 172}); 173