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 }> = ({ title, children }) => ( 76 <View style={styles.container}> 77 <Text style={styles.containerTitle}>{title}</Text> 78 {children} 79 </View> 80); 81 82const SnapshotTest = () => ( 83 <Container title="Snapshot"> 84 <View style={{ flexDirection: 'row', alignSelf: 'stretch', justifyContent: 'space-evenly' }}> 85 <LinearGradient 86 colors={['white', 'red']} 87 start={[0.5, 0.5]} 88 end={[1, 1]} 89 style={{ 90 width: 100, 91 maxHeight: 200, 92 minHeight: 200, 93 borderWidth: 1, 94 marginVertical: 20, 95 borderColor: 'black', 96 }} 97 /> 98 <Image 99 source={require('../../assets/images/confusing_gradient.png')} 100 style={{ width: 100, height: 200, marginVertical: 20 }} 101 /> 102 </View> 103 <Text style={{ marginHorizontal: 20 }}>The gradients above should look the same.</Text> 104 </Container> 105); 106 107const ControlPointTest: React.FunctionComponent<{ 108 start?: [number, number]; 109 end?: [number, number]; 110}> = ({ start = [0.5, 0], end = [0, 1] }) => { 111 const startInfo = `start={[${start.map(point => +point.toFixed(2)).join(', ')}]}`; 112 const endInfo = `end={[${end.map(point => +point.toFixed(2)).join(', ')}]}`; 113 114 return ( 115 <Container title="Control Points"> 116 <View> 117 {[startInfo, endInfo].map((pointInfo, index) => ( 118 <MonoText key={'--' + index}>{pointInfo}</MonoText> 119 ))} 120 </View> 121 <LinearGradient 122 start={start} 123 end={end} 124 locations={[0.5, 0.5]} 125 colors={['blue', 'lime']} 126 style={styles.gradient} 127 /> 128 </Container> 129 ); 130}; 131 132const ColorsTest = ({ colors }: { colors: string[] }) => { 133 const info = colors.map(value => `"${value}"`).join(', '); 134 return ( 135 <Container title="Colors"> 136 <MonoText>{`colors={[${info}]}`}</MonoText> 137 <LinearGradient colors={colors} style={styles.gradient} /> 138 </Container> 139 ); 140}; 141 142const LocationsTest: React.FunctionComponent<{ locations: number[] }> = ({ locations }) => { 143 const locationsInfo = locations.map(location => +location.toFixed(2)).join(', '); 144 return ( 145 <Container title="Locations"> 146 <MonoText>{`locations={[${locationsInfo}]}`}</MonoText> 147 <LinearGradient colors={['red', 'blue']} locations={locations} style={styles.gradient} /> 148 </Container> 149 ); 150}; 151 152const styles = StyleSheet.create({ 153 container: { 154 padding: 8, 155 flexShrink: 0, 156 }, 157 containerTitle: { 158 fontSize: 14, 159 fontWeight: '600', 160 textAlign: 'center', 161 marginBottom: 8, 162 }, 163 gradient: { 164 flex: 1, 165 flexShrink: 0, 166 minHeight: 200, 167 maxHeight: 200, 168 }, 169}); 170