1import * as Maps from 'expo-maps'; 2import React, { useContext, useRef, useState } from 'react'; 3import { 4 StyleSheet, 5 View, 6 Text, 7 Platform, 8 TextInput, 9 TouchableOpacity, 10 ScrollView, 11 KeyboardAvoidingView, 12} from 'react-native'; 13import { Snackbar } from 'react-native-paper'; 14 15import ProviderContext from '../context/ProviderContext'; 16 17export default function MapMoveExample() { 18 const provider = useContext(ProviderContext); 19 const ref = useRef<Maps.ExpoMap>(null); 20 21 const [snackbarText, setSnackbarText] = useState<string | undefined>(); 22 const [animateCamera, setAnimateCamera] = useState<boolean>(true); 23 const [animationDuration, setAnimationDuration] = useState<number>(1000); 24 const [zoom, setZoom] = useState<number>(4); 25 const [bearing, setBearing] = useState<number>(0); 26 const [tilt, setTilt] = useState<number>(0); 27 return ( 28 <KeyboardAvoidingView 29 behavior="position" 30 contentContainerStyle={{ flex: 1 }} 31 style={{ flex: 1 }} 32 keyboardVerticalOffset={92} 33 enabled={Platform.OS === 'ios'}> 34 <View style={styles.container}> 35 <Maps.ExpoMap 36 style={{ 37 flex: 1, 38 width: '100%', 39 overflow: 'hidden', 40 }} 41 ref={ref} 42 provider={provider} 43 onMapPress={async (event) => { 44 await ref.current 45 ?.moveCamera({ 46 target: { 47 latitude: 48 event.nativeEvent.latitude ?? 49 // @ts-ignore 50 // .payload won't be necessary when the Record conversion for iOS gets implemented 51 event.nativeEvent.payload.latitude, 52 longitude: 53 event.nativeEvent.longitude ?? 54 // @ts-ignore 55 event.nativeEvent.payload.longitude, 56 }, 57 bearing, 58 tilt, 59 zoom, 60 duration: animationDuration, 61 animate: animateCamera, 62 }) 63 .then((result) => setSnackbarText('Move ended at:' + JSON.stringify(result))); 64 }} 65 /> 66 <Snackbar 67 visible={snackbarText !== undefined} 68 onDismiss={() => setSnackbarText(undefined)} 69 style={{ backgroundColor: 'white' }} 70 wrapperStyle={styles.snackbar} 71 duration={2000}> 72 <Text style={{ color: 'black' }}>{snackbarText}</Text> 73 </Snackbar> 74 <ScrollView 75 style={{ maxHeight: 200, width: '100%' }} 76 contentContainerStyle={{ flexGrow: 1, margin: 0, padding: 0 }}> 77 <View style={[styles.controls]}> 78 <Text style={styles.title}>Press on the map to move the camera!</Text> 79 80 <TouchableOpacity onPress={() => setAnimateCamera(!animateCamera)}> 81 <View 82 style={[ 83 styles.button, 84 styles.shadow, 85 { backgroundColor: animateCamera ? 'lightgreen' : '#FF7F7F' }, 86 ]}> 87 <Text>Animate Camera:{animateCamera ? 'On' : 'Off'}</Text> 88 </View> 89 </TouchableOpacity> 90 91 {animateCamera && ( 92 <Input 93 valueText="Animation Duration: " 94 initialValue="1000" 95 onValueChange={(value) => setAnimationDuration(parseInt(value, 10))} 96 /> 97 )} 98 <View style={{ flexDirection: 'row' }}> 99 <Input 100 valueText="Zoom: " 101 initialValue="4" 102 onValueChange={(value) => setZoom(parseFloat(value))} 103 /> 104 <Input 105 valueText="Bearing: " 106 initialValue="0" 107 onValueChange={(value) => setBearing(parseFloat(value))} 108 /> 109 </View> 110 <Input 111 valueText="Tilt: " 112 initialValue="0" 113 onValueChange={(value) => setTilt(parseFloat(value))} 114 /> 115 </View> 116 </ScrollView> 117 </View> 118 </KeyboardAvoidingView> 119 ); 120} 121 122const styles = StyleSheet.create({ 123 container: { 124 flex: 1, 125 backgroundColor: 'white', 126 }, 127 controls: { 128 flex: 1, 129 backgroundColor: 'white', 130 alignItems: 'center', 131 }, 132 snackbar: { 133 top: 0, 134 }, 135 eventsList: { 136 padding: 20, 137 }, 138 title: { 139 fontSize: 18, 140 fontWeight: '400', 141 paddingVertical: 20, 142 }, 143 button: { 144 paddingVertical: 12, 145 paddingHorizontal: 20, 146 borderRadius: 30, 147 alignItems: 'center', 148 marginBottom: 10, 149 }, 150 inputContainer: { 151 alignItems: 'center', 152 paddingVertical: 12, 153 paddingHorizontal: 20, 154 borderRadius: 30, 155 }, 156 input: { 157 borderColor: 'lightgray', 158 borderWidth: 1, 159 borderRadius: 5, 160 paddingVertical: 5, 161 paddingHorizontal: 10, 162 textAlign: 'center', 163 }, 164 shadow: { 165 shadowOpacity: 0.1, 166 shadowRadius: 5, 167 elevation: 10, 168 shadowColor: 'rgba(0,0,0,0.56)', 169 }, 170}); 171 172const Input = (props: { 173 valueText: string; 174 initialValue: string; 175 onValueChange: (value: string) => void; 176}) => ( 177 <View style={styles.inputContainer}> 178 <View style={{ flexDirection: 'row', alignItems: 'center' }}> 179 <Text>{props.valueText}</Text> 180 <TextInput 181 style={styles.input} 182 onChangeText={(text) => props.onValueChange(text)} 183 defaultValue={props.initialValue} 184 keyboardType="decimal-pad" 185 /> 186 </View> 187 </View> 188); 189