|
|
@@ -437,6 +437,9 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
|
|
|
const [center, setCenter] = useState<number[] | null>(null);
|
|
|
const [isZooming, setIsZooming] = useState(true);
|
|
|
const hideTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
|
+ const [renderCamera, setRenderCamera] = useState(Platform.OS === 'ios');
|
|
|
+ const isAnimatingRef = useRef(false);
|
|
|
+ const animationTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
|
|
|
|
const [markerCoords, setMarkerCoords] = useState<any>(null);
|
|
|
const [refreshInterval, setRefreshInterval] = useState(0);
|
|
|
@@ -766,10 +769,11 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
|
|
|
setMarkerCoords([route.params.lon, route.params.lat]);
|
|
|
const timeoutId = setTimeout(() => {
|
|
|
if (cameraRef.current) {
|
|
|
- cameraRef.current.setCamera({
|
|
|
+ cameraController.setCamera({
|
|
|
centerCoordinate: [route.params?.lon, route.params?.lat],
|
|
|
zoomLevel: 15,
|
|
|
- animationDuration: 800
|
|
|
+ animationDuration: 800,
|
|
|
+ animationMode: 'flyTo'
|
|
|
});
|
|
|
} else {
|
|
|
console.warn('Camera ref is not available.');
|
|
|
@@ -885,6 +889,149 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
|
|
|
const cameraRef = useRef<MapLibreRN.CameraRef>(null);
|
|
|
const shapeSourceRef = useRef<MapLibreRN.ShapeSourceRef>(null);
|
|
|
|
|
|
+ const cameraController = {
|
|
|
+ setCamera: useCallback((config: any) => {
|
|
|
+ isAnimatingRef.current = true;
|
|
|
+
|
|
|
+ if (animationTimeoutRef.current) {
|
|
|
+ clearTimeout(animationTimeoutRef.current);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (Platform.OS === 'android') {
|
|
|
+ setRenderCamera(true);
|
|
|
+
|
|
|
+ requestAnimationFrame(() => {
|
|
|
+ cameraRef.current?.setCamera(config);
|
|
|
+ });
|
|
|
+
|
|
|
+ animationTimeoutRef.current = setTimeout(
|
|
|
+ () => {
|
|
|
+ isAnimatingRef.current = false;
|
|
|
+ setRenderCamera(false);
|
|
|
+ },
|
|
|
+ (config.animationDuration || 1000) + 200
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ cameraRef.current?.setCamera(config);
|
|
|
+
|
|
|
+ animationTimeoutRef.current = setTimeout(
|
|
|
+ () => {
|
|
|
+ isAnimatingRef.current = false;
|
|
|
+ },
|
|
|
+ (config.animationDuration || 1000) + 100
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }, []),
|
|
|
+
|
|
|
+ fitBounds: useCallback((ne: number[], sw: number[], padding: number[], duration: number) => {
|
|
|
+ isAnimatingRef.current = true;
|
|
|
+
|
|
|
+ if (animationTimeoutRef.current) {
|
|
|
+ clearTimeout(animationTimeoutRef.current);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (Platform.OS === 'android') {
|
|
|
+ setRenderCamera(true);
|
|
|
+
|
|
|
+ requestAnimationFrame(() => {
|
|
|
+ cameraRef.current?.fitBounds(ne, sw, padding, duration);
|
|
|
+ });
|
|
|
+
|
|
|
+ animationTimeoutRef.current = setTimeout(() => {
|
|
|
+ isAnimatingRef.current = false;
|
|
|
+ setRenderCamera(false);
|
|
|
+ }, duration + 200);
|
|
|
+ } else {
|
|
|
+ cameraRef.current?.fitBounds(ne, sw, padding, duration);
|
|
|
+
|
|
|
+ animationTimeoutRef.current = setTimeout(() => {
|
|
|
+ isAnimatingRef.current = false;
|
|
|
+ }, duration + 100);
|
|
|
+ }
|
|
|
+ }, []),
|
|
|
+
|
|
|
+ flyTo: useCallback((coordinates: number[], duration: number = 1000) => {
|
|
|
+ isAnimatingRef.current = true;
|
|
|
+
|
|
|
+ if (animationTimeoutRef.current) {
|
|
|
+ clearTimeout(animationTimeoutRef.current);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (Platform.OS === 'android') {
|
|
|
+ setRenderCamera(true);
|
|
|
+
|
|
|
+ requestAnimationFrame(() => {
|
|
|
+ cameraRef.current?.flyTo(coordinates, duration);
|
|
|
+ });
|
|
|
+
|
|
|
+ animationTimeoutRef.current = setTimeout(() => {
|
|
|
+ isAnimatingRef.current = false;
|
|
|
+ setRenderCamera(false);
|
|
|
+ }, duration + 200);
|
|
|
+ } else {
|
|
|
+ cameraRef.current?.flyTo(coordinates, duration);
|
|
|
+
|
|
|
+ animationTimeoutRef.current = setTimeout(() => {
|
|
|
+ isAnimatingRef.current = false;
|
|
|
+ }, duration + 100);
|
|
|
+ }
|
|
|
+ }, []),
|
|
|
+
|
|
|
+ moveTo: useCallback((coordinates: number[], duration: number = 0) => {
|
|
|
+ isAnimatingRef.current = true;
|
|
|
+
|
|
|
+ if (animationTimeoutRef.current) {
|
|
|
+ clearTimeout(animationTimeoutRef.current);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (Platform.OS === 'android') {
|
|
|
+ setRenderCamera(true);
|
|
|
+
|
|
|
+ requestAnimationFrame(() => {
|
|
|
+ cameraRef.current?.moveTo(coordinates, duration);
|
|
|
+ });
|
|
|
+
|
|
|
+ animationTimeoutRef.current = setTimeout(() => {
|
|
|
+ isAnimatingRef.current = false;
|
|
|
+ setRenderCamera(false);
|
|
|
+ }, duration + 200);
|
|
|
+ } else {
|
|
|
+ cameraRef.current?.moveTo(coordinates, duration);
|
|
|
+
|
|
|
+ animationTimeoutRef.current = setTimeout(() => {
|
|
|
+ isAnimatingRef.current = false;
|
|
|
+ }, duration + 100);
|
|
|
+ }
|
|
|
+ }, []),
|
|
|
+
|
|
|
+ zoomTo: useCallback((zoomLevel: number, duration: number = 1000) => {
|
|
|
+ isAnimatingRef.current = true;
|
|
|
+
|
|
|
+ if (animationTimeoutRef.current) {
|
|
|
+ clearTimeout(animationTimeoutRef.current);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (Platform.OS === 'android') {
|
|
|
+ setRenderCamera(true);
|
|
|
+
|
|
|
+ requestAnimationFrame(() => {
|
|
|
+ cameraRef.current?.zoomTo(zoomLevel, duration);
|
|
|
+ });
|
|
|
+
|
|
|
+ animationTimeoutRef.current = setTimeout(() => {
|
|
|
+ isAnimatingRef.current = false;
|
|
|
+ setRenderCamera(false);
|
|
|
+ }, duration + 200);
|
|
|
+ } else {
|
|
|
+ cameraRef.current?.zoomTo(zoomLevel, duration);
|
|
|
+
|
|
|
+ animationTimeoutRef.current = setTimeout(() => {
|
|
|
+ isAnimatingRef.current = false;
|
|
|
+ }, duration + 100);
|
|
|
+ }
|
|
|
+ }, [])
|
|
|
+ };
|
|
|
+
|
|
|
useEffect(() => {
|
|
|
if (userInfo) {
|
|
|
setUserInfoData(JSON.parse(userInfo));
|
|
|
@@ -933,10 +1080,11 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
|
|
|
if (initialRegion && !route.params?.id) {
|
|
|
const timeoutId = setTimeout(() => {
|
|
|
if (cameraRef.current) {
|
|
|
- cameraRef.current.setCamera({
|
|
|
+ cameraController.setCamera({
|
|
|
centerCoordinate: [initialRegion.longitude, initialRegion.latitude],
|
|
|
zoomLevel: Math.log2(360 / initialRegion.latitudeDelta),
|
|
|
- animationDuration: 500
|
|
|
+ animationDuration: 500,
|
|
|
+ animationMode: 'flyTo'
|
|
|
});
|
|
|
} else {
|
|
|
console.warn('Camera ref is not available.');
|
|
|
@@ -948,7 +1096,7 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
|
|
|
}, [initialRegion]);
|
|
|
|
|
|
const handleMapChange = async () => {
|
|
|
- if (!mapRef.current) return;
|
|
|
+ if (!mapRef.current || isAnimatingRef.current) return;
|
|
|
if (hideTimer.current) clearTimeout(hideTimer.current);
|
|
|
|
|
|
setIsZooming(true);
|
|
|
@@ -969,6 +1117,14 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
|
|
|
});
|
|
|
};
|
|
|
|
|
|
+ useEffect(() => {
|
|
|
+ return () => {
|
|
|
+ if (animationTimeoutRef.current) {
|
|
|
+ clearTimeout(animationTimeoutRef.current);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }, []);
|
|
|
+
|
|
|
const onMapPress = async (event: any) => {
|
|
|
if (!mapRef.current) return;
|
|
|
if (selectedMarker || selectedUser) {
|
|
|
@@ -1019,7 +1175,7 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
|
|
|
const region = regionsList.data.find((region) => region.id === +foundRegion);
|
|
|
if (region) {
|
|
|
const bounds = turf.bbox(region.bbox);
|
|
|
- cameraRef.current?.fitBounds(
|
|
|
+ cameraController.fitBounds(
|
|
|
[bounds[2], bounds[3]],
|
|
|
[bounds[0], bounds[1]],
|
|
|
[10, 10, 50, 10],
|
|
|
@@ -1042,7 +1198,7 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
|
|
|
const region = countriesList.data.find((region) => region.id === +foundRegion);
|
|
|
if (region) {
|
|
|
const bounds = turf.bbox(region.bbox);
|
|
|
- cameraRef.current?.fitBounds(
|
|
|
+ cameraController.fitBounds(
|
|
|
[bounds[2], bounds[3]],
|
|
|
[bounds[0], bounds[1]],
|
|
|
[10, 10, 50, 10],
|
|
|
@@ -1065,7 +1221,7 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
|
|
|
const region = dareList.data.find((region) => region.id === +foundRegion);
|
|
|
if (region) {
|
|
|
const bounds = turf.bbox(region.bbox);
|
|
|
- cameraRef.current?.fitBounds(
|
|
|
+ cameraController.fitBounds(
|
|
|
[bounds[2], bounds[3]],
|
|
|
[bounds[0], bounds[1]],
|
|
|
[10, 10, 50, 10],
|
|
|
@@ -1148,7 +1304,7 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
|
|
|
setLocation(currentLocation.coords);
|
|
|
|
|
|
if (currentLocation.coords) {
|
|
|
- cameraRef.current?.flyTo(
|
|
|
+ cameraController.flyTo(
|
|
|
[currentLocation.coords.longitude, currentLocation.coords.latitude],
|
|
|
1000
|
|
|
);
|
|
|
@@ -1254,7 +1410,7 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
|
|
|
const region = regionsList.data.find((region) => region.id === +id);
|
|
|
if (region) {
|
|
|
const bounds = turf.bbox(region.bbox);
|
|
|
- cameraRef.current?.fitBounds(
|
|
|
+ cameraController.fitBounds(
|
|
|
[bounds[2], bounds[3]],
|
|
|
[bounds[0], bounds[1]],
|
|
|
[10, 10, 50, 10],
|
|
|
@@ -1278,7 +1434,7 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
|
|
|
const region = countriesList.data.find((region) => region.id === +id);
|
|
|
if (region) {
|
|
|
const bounds = turf.bbox(region.bbox);
|
|
|
- cameraRef.current?.fitBounds(
|
|
|
+ cameraController.fitBounds(
|
|
|
[bounds[2], bounds[3]],
|
|
|
[bounds[0], bounds[1]],
|
|
|
[10, 10, 50, 10],
|
|
|
@@ -1302,7 +1458,7 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
|
|
|
const region = dareList.data.find((region) => region.id === +id);
|
|
|
if (region) {
|
|
|
const bounds = turf.bbox(region.bbox);
|
|
|
- cameraRef.current?.fitBounds(
|
|
|
+ cameraController.fitBounds(
|
|
|
[bounds[2], bounds[3]],
|
|
|
[bounds[0], bounds[1]],
|
|
|
[10, 10, 50, 10],
|
|
|
@@ -1694,7 +1850,7 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
|
|
|
);
|
|
|
const newZoom = zoom ?? 2;
|
|
|
|
|
|
- cameraRef.current?.setCamera({
|
|
|
+ cameraController.setCamera({
|
|
|
centerCoordinate: clusterCoordinates,
|
|
|
zoomLevel: newZoom,
|
|
|
animationDuration: 500,
|
|
|
@@ -1812,7 +1968,9 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
|
|
|
{selectedMarker && (
|
|
|
<MarkerItem marker={selectedMarker} toggleSeries={toggleSeries} token={token} />
|
|
|
)}
|
|
|
- {Platform.OS === 'ios' && <MapLibreRN.Camera ref={cameraRef} />}
|
|
|
+ {(renderCamera || Platform.OS === 'ios') && (
|
|
|
+ <MapLibreRN.Camera ref={cameraRef} followUserLocation={undefined} animationMode="flyTo" />
|
|
|
+ )}
|
|
|
{location && (
|
|
|
<MapLibreRN.UserLocation
|
|
|
animated={true}
|
|
|
@@ -1821,7 +1979,7 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
|
|
|
const currentZoom = await mapRef.current?.getZoom();
|
|
|
const newZoom = (currentZoom || 0) + 2;
|
|
|
|
|
|
- cameraRef.current?.setCamera({
|
|
|
+ cameraController.setCamera({
|
|
|
centerCoordinate: [location.longitude, location.latitude],
|
|
|
zoomLevel: newZoom,
|
|
|
animationDuration: 500,
|