Created
June 30, 2021 03:34
-
-
Save hungtrn75/7c8dfb96a1f2e5c8a289c134e4ee7d98 to your computer and use it in GitHub Desktop.
react-native-maps
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, {useRef, useState} from 'react'; | |
import { | |
Dimensions, | |
Image, | |
InteractionManager, | |
Platform, | |
StyleSheet, | |
} from 'react-native'; | |
import {AnimatedRegion, Marker, Polyline} from 'react-native-maps'; | |
import Animated, { | |
runOnJS, | |
useAnimatedStyle, | |
useDerivedValue, | |
useSharedValue, | |
withTiming, | |
} from 'react-native-reanimated'; | |
const screen = Dimensions.get('window'); | |
const ASPECT_RATIO = screen.width / screen.height; | |
const LATITUDE_DELTA = 0.0922; | |
const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; | |
const getRotation = (prevPos, curPos) => { | |
if (!curPos) { | |
return 0; | |
} | |
const xDiff = curPos.latitude - prevPos.latitude; | |
const yDiff = curPos.longitude - prevPos.longitude; | |
return (Math.atan2(yDiff, xDiff) * 180.0) / Math.PI; | |
}; | |
const AnimatePolyline = ({Direction, index, isPlaying}) => { | |
const [polylinePath, setPolylinePath] = useState([]); | |
const marker = useRef(); | |
const [coordinate, setCoordinate] = useState( | |
new AnimatedRegion({ | |
latitude: Direction[0].latitude, | |
longitude: Direction[0].longitude, | |
longitudeDelta: LONGITUDE_DELTA, | |
latitudeDelta: LATITUDE_DELTA, | |
}), | |
); | |
const rotate = useSharedValue(getRotation(Direction[0], Direction[1])); | |
const getData = (data = []) => { | |
'worklet'; | |
const clone = JSON.parse(JSON.stringify(data)); | |
return clone.map(e => ({...e})); | |
}; | |
const animatePolylineStart = cIndex => { | |
if (cIndex < 1) return; | |
const duration = isPlaying.current ? 250 : 50; | |
if (cIndex <= Direction.length - 1) { | |
const nC = Direction[cIndex]; | |
let rotation = rotate.value; | |
if (cIndex >= Direction.length - 1) { | |
} else { | |
rotation = getRotation(Direction[cIndex - 1], nC); | |
} | |
rotate.value = withTiming(rotation); | |
coordinate | |
.timing(nC, { | |
duration: duration, | |
}) | |
.start(); | |
} | |
const updatePath = () => { | |
const nP = Direction.slice( | |
0, | |
cIndex == Direction.length - 1 ? cIndex + 1 : cIndex, | |
); | |
setPolylinePath(nP); | |
}; | |
if (isPlaying.current) { | |
updatePath(); | |
} else { | |
InteractionManager.runAfterInteractions(updatePath); | |
} | |
}; | |
useDerivedValue(() => { | |
runOnJS(animatePolylineStart)(index.value); | |
}, [index]); | |
const style = useAnimatedStyle(() => { | |
return { | |
transform: [ | |
{ | |
rotate: `${rotate.value}deg`, | |
}, | |
], | |
}; | |
}); | |
return ( | |
<> | |
{polylinePath.length > 0 ? ( | |
<Polyline | |
coordinates={polylinePath} | |
strokeColor="orange" | |
strokeWidth={5} | |
/> | |
) : null} | |
<Marker.Animated ref={marker} coordinate={coordinate} anchor={anchor}> | |
<Animated.View style={style}> | |
<Image source={require('./navigate.png')} style={styles.icon} /> | |
</Animated.View> | |
</Marker.Animated> | |
</> | |
); | |
}; | |
export default AnimatePolyline; | |
const anchor = { | |
x: 0.5, | |
y: 0.5, | |
}; | |
const styles = StyleSheet.create({ | |
icon: { | |
width: 20, | |
height: 20, | |
}, | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, {useCallback, useMemo, useRef, useState} from 'react'; | |
import {StyleSheet, View} from 'react-native'; | |
import MapView, {Polyline} from 'react-native-maps'; | |
import {useSharedValue} from 'react-native-reanimated'; | |
import {dimensions} from '../../constants/theme'; | |
import {useLoadDone} from '../../utils/hooks'; | |
import AnimatePolyline from './AnimatePolyline'; | |
import XemLaiSheet from './XemLaiSheet'; | |
import Direction from './routes.json'; | |
import BottomSheet from '@gorhom/bottom-sheet'; | |
const FDirection = Direction.map(el => ({ | |
latitude: el[1], | |
longitude: el[0], | |
})); | |
const DirectionRoutes = [...FDirection]; | |
const initialRegion = { | |
latitude: 20.998384443680877, | |
longitude: 105.84221005439758, | |
latitudeDelta: 0.0922, | |
longitudeDelta: 0.0421, | |
}; | |
const MapTracking = () => { | |
const mapview = useRef(); | |
const bottomSheetRef = useRef(null); | |
const sliderRef = useRef(null); | |
const isPlaying = useRef(false); | |
const [disable, setDisable] = useState(false); | |
const cIndex = useSharedValue(0); | |
const mounted = useLoadDone(); | |
const snapPoints = useMemo( | |
() => [100 + dimensions.BOTTOM_SPACE, 350 + dimensions.BOTTOM_SPACE], | |
[], | |
); | |
const onChange = index => { | |
if (index == 1) { | |
setDisable(true); | |
} else { | |
setDisable(false); | |
} | |
}; | |
const onSelect = useCallback(values => { | |
bottomSheetRef.current?.snapTo(0); | |
}, []); | |
if (!mounted) return null; | |
return ( | |
<View style={styles.container}> | |
<MapView | |
ref={mapview} | |
// provider="google" | |
initialRegion={initialRegion} | |
style={StyleSheet.absoluteFillObject} | |
showsUserLocation={false} | |
showsMyLocationButton={false} | |
rotateEnabled={false} | |
pitchEnabled={false} | |
// mapType={mapType} | |
> | |
<Polyline coordinates={FDirection} strokeColor="#666" strokeWidth={5} /> | |
<AnimatePolyline | |
Direction={DirectionRoutes} | |
index={cIndex} | |
isPlaying={isPlaying} | |
/> | |
</MapView> | |
<BottomSheet | |
// waitFor={[sliderRef]} | |
// enableContentPanningGesture={false} | |
// simultaneousHandlers={sliderRef} | |
ref={bottomSheetRef} | |
snapPoints={snapPoints} | |
activeOffsetY={[-1, 1]} | |
failOffsetX={[-5, 5]} | |
onChange={onChange}> | |
<XemLaiSheet | |
ref={sliderRef} | |
index={cIndex} | |
max={FDirection.length} | |
isPlaying={isPlaying} | |
data={DirectionRoutes} | |
disable={disable} | |
onSelect={onSelect} | |
/> | |
</BottomSheet> | |
</View> | |
); | |
}; | |
export default MapTracking; | |
const styles = StyleSheet.create({ | |
container: { | |
flex: 1, | |
}, | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
[105.84818601608276, 20.997933707711468], | |
[105.84773540496825, 20.997953740450114], | |
[105.8476173877716, 20.997743396560207], | |
[105.84759593009947, 20.997492986780994], | |
[105.84755301475523, 20.997202510910867], | |
[105.84749937057495, 20.996952100224355], | |
[105.84739208221434, 20.996711705570117], | |
[105.847327709198, 20.996541425789154], | |
[105.84674835205077, 20.996611541016613], | |
[105.84634065628052, 20.996691672664795], | |
[105.84587931632996, 20.996791837164547], | |
[105.84548234939575, 20.99690201803664], | |
[105.8447742462158, 20.99709233026055], | |
[105.844269990921, 20.997232560165017], | |
[105.8436369895935, 20.997362773529797], | |
[105.84300398826599, 20.997513019578786], | |
[105.84251046180725, 20.997683298251506], | |
[105.84240317344666, 20.997943724081136], | |
[105.84221005439758, 20.998384443680877], | |
[105.84208130836487, 20.998745031476464], | |
[105.84190964698792, 20.999145683561068], | |
[105.84173798561096, 20.999606432128886], | |
[105.84160923957825, 20.99990691955907], | |
[105.84145903587341, 21.000167341509236], | |
[105.84137320518494, 21.00040773059842], | |
[105.84135174751282, 21.00104876627674], | |
[105.841383934021, 21.001579621863783], | |
[105.8414375782013, 21.00172986366826], | |
[105.84168434143066, 21.001840040895395], | |
[105.84226369857788, 21.001840040895395], | |
[105.84269285202026, 21.001850057002923], | |
[105.84306836128235, 21.001820008678333], | |
[105.84349751472473, 21.001830024787196], | |
[105.8439266681671, 21.001830024787196], | |
[105.84428071975708, 21.001830024787196], | |
[105.84461331367491, 21.001809992568784], | |
[105.8449351787567, 21.001809992568784], | |
[105.84534287452698, 21.001799976458575], | |
[105.84571838378906, 21.001809992568784], | |
[105.84597587585449, 21.001799976458575], | |
[105.84622263908386, 21.001820008678333], | |
[105.84609389305115, 21.001980266339594], | |
[105.8459436893463, 21.002150539916123], | |
[105.84577202796936, 21.002320813298397], | |
[105.8456003665924, 21.002501102550237], | |
[105.84545016288757, 21.00266135948015], | |
[105.84568619728088, 21.002871696439488], | |
[105.84592223167418, 21.003031952971437], | |
[105.84609389305115, 21.00318219331385], | |
[105.84626555442809, 21.003292369468824], | |
[105.84642648696898, 21.003422577547102], | |
[105.84628701210022, 21.003602865467933], | |
[105.84644794464111, 21.00371304131233], | |
[105.84663033485413, 21.00385326499665], | |
[105.84678053855896, 21.00399348854922], | |
[105.84694147109985, 21.003823217075396], | |
[105.84713459014893, 21.003983472585546], | |
[105.84724187850952, 21.004043568357485], | |
[105.84752082824707, 21.00374308925579], | |
[105.84771394729613, 21.00356280150435], | |
[105.84786415100098, 21.003392529539116], | |
[105.84801435470581, 21.00330238547887], | |
[105.84826111793518, 21.00305198502583], | |
[105.8484435081482, 21.002851664360897], | |
[105.8486795425415, 21.002611279208033], | |
[105.84893703460693, 21.00234084544826], | |
[105.84864735603333, 21.002070411198428], | |
[105.84836840629578, 21.001860073109775], | |
[105.84799289703369, 21.001509508969946] | |
] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Slider from '@react-native-community/slider'; | |
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; | |
import {StyleSheet, Text, TouchableOpacity, View, Platform} from 'react-native'; | |
import Animated, {useAnimatedProps} from 'react-native-reanimated'; | |
import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; | |
import Block from '../../components/Block'; | |
import DatePicker from '../../components/DatePicker'; | |
import {colors, dimensions} from '../../constants/theme'; | |
import moment from 'moment'; | |
import BaseButton from '../../components/BaseButton'; | |
import Spacer from '../../components/Spacer'; | |
import DateWheelPicker from '../truc_tuyen/components/DateWheelPicker'; | |
const AnimatedSlider = Animated.createAnimatedComponent(Slider); | |
const XemLaiSheet = React.forwardRef( | |
({index, isPlaying, max, data, disable, onSelect}, ref) => { | |
const interval = useRef(null); | |
const datePickerRef = useRef(null); | |
const [playing, setPlaying] = useState(false); | |
const [item, setItem] = useState(data[0]); | |
const [startTime, setStartTime] = useState(moment().toDate()); | |
const [endTime, setEndTime] = useState(moment().toDate()); | |
const onPlay = () => { | |
if (isPlaying.current) { | |
clear(); | |
} else { | |
setPlayStatus(true); | |
interval.current = setInterval(onStartImp, 500); | |
} | |
}; | |
const setPlayStatus = val => { | |
isPlaying.current = val; | |
setPlaying(val); | |
}; | |
const onStartImp = () => { | |
if (index.value < max - 1) { | |
index.value += 1; | |
} else { | |
index.value = 0; | |
clear(); | |
} | |
setItem(data[index.value]); | |
}; | |
const clear = () => { | |
setPlayStatus(false); | |
if (interval.current) clearInterval(interval.current); | |
}; | |
const onValueChange = val => { | |
index.value = val; | |
setItem(data[index.value]); | |
}; | |
const onSlidingStart = () => { | |
clear(); | |
setPlayStatus(false); | |
}; | |
useEffect(() => { | |
if (disable) { | |
clear(); | |
} | |
}, [disable]); | |
useEffect(() => { | |
return () => { | |
if (interval.current) { | |
clearInterval(interval.current); | |
} | |
}; | |
}, []); | |
const onDateSelect = useCallback(() => { | |
const values = datePickerRef.current?.getValues(); | |
setStartTime(values[0]); | |
setEndTime(values[1]); | |
onSelect(values); | |
}, []); | |
const onCancelSelect = useCallback(() => { | |
onSelect(null); | |
}, [onSelect]); | |
const animatedSliderProps = useAnimatedProps(() => { | |
return { | |
value: index.value, | |
}; | |
}, [index]); | |
const btnStyle = useMemo( | |
() => ({borderColor: disable ? colors.GRAY : colors.IOS_BTN}), | |
[disable], | |
); | |
const txtBtnStyle = useMemo( | |
() => ({ | |
color: disable ? colors.GRAY : colors.IOS_BTN, | |
letterSpacing: 0.6, | |
}), | |
[disable], | |
); | |
return ( | |
<View> | |
<Block padding={[0, 15]}> | |
<Text>{JSON.stringify(item)}</Text> | |
<Block row center> | |
<TouchableOpacity | |
onPress={onPlay} | |
disabled={disable} | |
style={[styles.t1, btnStyle]}> | |
<Icon | |
name={playing ? 'pause' : 'play'} | |
size={20} | |
color={disable ? colors.GRAY : colors.IOS_BTN} | |
/> | |
</TouchableOpacity> | |
<AnimatedSlider | |
ref={ref} | |
style={styles.s1} | |
minimumValue={0} | |
maximumValue={max - 1} | |
step={1} | |
animatedProps={animatedSliderProps} | |
onValueChange={onValueChange} | |
onSlidingStart={onSlidingStart} | |
disabled={disable} | |
/> | |
<TouchableOpacity onPress={onPlay} style={[styles.t1, btnStyle]}> | |
<Text style={txtBtnStyle}>1x</Text> | |
</TouchableOpacity> | |
</Block> | |
</Block> | |
<DateWheelPicker | |
ref={datePickerRef} | |
startTime={startTime} | |
endTime={endTime} | |
/> | |
<View style={styles.v1}> | |
<BaseButton | |
label={'HUỶ'} | |
style={styles.b1} | |
onPress={onCancelSelect} | |
/> | |
<Spacer width={15} /> | |
<BaseButton label={'CHỌN'} style={styles.b2} onPress={onDateSelect} /> | |
</View> | |
</View> | |
); | |
}, | |
); | |
export default XemLaiSheet; | |
const styles = StyleSheet.create({ | |
b1: { | |
flex: 1, | |
backgroundColor: colors.ERROR, | |
}, | |
b2: { | |
flex: 1, | |
}, | |
s1: {flex: 1, marginHorizontal: 10}, | |
t1: { | |
width: 28, | |
height: 28, | |
borderWidth: 1, | |
borderRadius: 14, | |
justifyContent: 'center', | |
alignItems: 'center', | |
}, | |
v1: { | |
marginTop: -20, | |
flexDirection: 'row', | |
paddingHorizontal: 15, | |
}, | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment