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 this._interval = setInterval(() => { 39 this.setState((state) => ({ 40 count: state.count + 1, 41 colorTop: incrementColor(state.colorTop, 1), 42 colorBottom: incrementColor(state.colorBottom, -1), 43 })); 44 }, 100); 45 } 46 47 componentWillUnmount() { 48 clearInterval(this._interval); 49 } 50 51 render() { 52 const location = Math.sin(this.state.count / 100) * 0.5; 53 const position = Math.sin(this.state.count / 100); 54 return ( 55 <ScrollView 56 style={{ flex: 1 }} 57 contentContainerStyle={{ 58 alignItems: 'stretch', 59 paddingVertical: 10, 60 }}> 61 <AnimatedLinearGradient 62 style={{ display: 'none' }} 63 colors={[this.state.colorTop, this.state.colorBottom]} 64 /> 65 <ColorsTest colors={[this.state.colorTop, this.state.colorBottom]} /> 66 <LocationsTest locations={[location, 1.0 - location]} /> 67 <ControlPointTest start={[position, 0]} /> 68 {Platform.OS !== 'web' && <SnapshotTest />} 69 </ScrollView> 70 ); 71 } 72} 73 74const Container: React.FunctionComponent<{ title: string; children?: React.ReactNode }> = ({ 75 title, 76 children, 77}) => ( 78 <View style={styles.container}> 79 <Text style={styles.containerTitle}>{title}</Text> 80 {children} 81 </View> 82); 83 84const SnapshotTest = () => ( 85 <Container title="Snapshot"> 86 <View style={{ flexDirection: 'row', alignSelf: 'stretch', justifyContent: 'space-evenly' }}> 87 <LinearGradient 88 colors={['white', 'red']} 89 start={[0.5, 0.5]} 90 end={[1, 1]} 91 style={{ 92 width: 100, 93 maxHeight: 200, 94 minHeight: 200, 95 borderWidth: 1, 96 marginVertical: 20, 97 borderColor: 'black', 98 }} 99 /> 100 <Image 101 source={require('../../assets/images/confusing_gradient.png')} 102 style={{ width: 100, height: 200, marginVertical: 20 }} 103 /> 104 </View> 105 <Text style={{ marginHorizontal: 20 }}>The gradients above should look the same.</Text> 106 </Container> 107); 108 109const ControlPointTest: React.FunctionComponent<{ 110 start?: [number, number]; 111 end?: [number, number]; 112}> = ({ start = [0.5, 0], end = [0, 1] }) => { 113 const startInfo = `start={[${start.map((point) => +point.toFixed(2)).join(', ')}]}`; 114 const endInfo = `end={[${end.map((point) => +point.toFixed(2)).join(', ')}]}`; 115 116 return ( 117 <Container title="Control Points"> 118 <View> 119 {[startInfo, endInfo].map((pointInfo, index) => ( 120 <MonoText key={'--' + index}>{pointInfo}</MonoText> 121 ))} 122 </View> 123 <LinearGradient 124 start={start} 125 end={end} 126 locations={[0.5, 0.5]} 127 colors={['blue', 'lime']} 128 style={styles.gradient} 129 /> 130 </Container> 131 ); 132}; 133 134const ColorsTest = ({ colors }: { colors: string[] }) => { 135 const info = colors.map((value) => `"${value}"`).join(', '); 136 return ( 137 <Container title="Colors"> 138 <MonoText>{`colors={[${info}]}`}</MonoText> 139 <LinearGradient colors={colors} style={styles.gradient} /> 140 </Container> 141 ); 142}; 143 144const LocationsTest: React.FunctionComponent<{ locations: number[] }> = ({ locations }) => { 145 const locationsInfo = locations.map((location) => +location.toFixed(2)).join(', '); 146 return ( 147 <Container title="Locations"> 148 <MonoText>{`locations={[${locationsInfo}]}`}</MonoText> 149 <LinearGradient colors={['red', 'blue']} locations={locations} style={styles.gradient} /> 150 </Container> 151 ); 152}; 153 154const styles = StyleSheet.create({ 155 container: { 156 padding: 8, 157 flexShrink: 0, 158 }, 159 containerTitle: { 160 fontSize: 14, 161 fontWeight: '600', 162 textAlign: 'center', 163 marginBottom: 8, 164 }, 165 gradient: { 166 flex: 1, 167 flexShrink: 0, 168 minHeight: 200, 169 maxHeight: 200, 170 }, 171}); 172