feat: implement map fly-to functionality with custom events for improved navigation experience
This commit is contained in:
parent
a2f51e6837
commit
6327d7b886
|
@ -256,19 +256,40 @@ export default function SearchTooltip({ onControlChange, activeControl, crimes =
|
|||
const handleFlyToIncident = () => {
|
||||
if (!selectedSuggestion || !selectedSuggestion.locations.latitude || !selectedSuggestion.locations.longitude) return;
|
||||
|
||||
const flyToEvent = new CustomEvent('incident_click', {
|
||||
// First, trigger a separate mapbox_fly_to event to handle the camera movement
|
||||
const flyToMapEvent = new CustomEvent('mapbox_fly_to', {
|
||||
detail: {
|
||||
longitude: selectedSuggestion.locations.longitude,
|
||||
latitude: selectedSuggestion.locations.latitude,
|
||||
id: selectedSuggestion.id,
|
||||
zoom: 15,
|
||||
description: selectedSuggestion.description,
|
||||
status: selectedSuggestion.status
|
||||
bearing: 0,
|
||||
pitch: 45,
|
||||
duration: 2000,
|
||||
},
|
||||
bubbles: true
|
||||
});
|
||||
|
||||
document.dispatchEvent(flyToEvent);
|
||||
document.dispatchEvent(flyToMapEvent);
|
||||
|
||||
// Wait for the fly animation to complete before showing the popup
|
||||
setTimeout(() => {
|
||||
// Then trigger the incident_click event to show the popup
|
||||
const incidentEvent = new CustomEvent('incident_click', {
|
||||
detail: {
|
||||
id: selectedSuggestion.id,
|
||||
longitude: selectedSuggestion.locations.longitude,
|
||||
latitude: selectedSuggestion.locations.latitude,
|
||||
description: selectedSuggestion.description,
|
||||
status: selectedSuggestion.status,
|
||||
timestamp: selectedSuggestion.timestamp,
|
||||
crime_categories: selectedSuggestion.crime_categories
|
||||
},
|
||||
bubbles: true
|
||||
});
|
||||
|
||||
document.dispatchEvent(incidentEvent);
|
||||
}, 2100); // Slightly longer than the fly animation duration
|
||||
|
||||
setShowInfoBox(false);
|
||||
setSelectedSuggestion(null);
|
||||
toggleSearch();
|
||||
|
|
|
@ -185,6 +185,46 @@ export default function CrimeMap() {
|
|||
};
|
||||
}, [filteredCrimes]);
|
||||
|
||||
// Set up event listener for fly-to map control
|
||||
useEffect(() => {
|
||||
const handleMapFlyTo = (e: CustomEvent) => {
|
||||
if (!e.detail) {
|
||||
console.error("Invalid fly-to data:", e.detail);
|
||||
return;
|
||||
}
|
||||
|
||||
const { longitude, latitude, zoom, bearing, pitch, duration } = e.detail;
|
||||
|
||||
// Find the map instance
|
||||
const mapInstance = mapContainerRef.current?.querySelector('.mapboxgl-map');
|
||||
if (!mapInstance) {
|
||||
console.error("Map instance not found");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create and dispatch a custom event that MapView component will listen for
|
||||
const mapboxEvent = new CustomEvent('mapbox_fly', {
|
||||
detail: {
|
||||
center: [longitude, latitude],
|
||||
zoom: zoom || 15,
|
||||
bearing: bearing || 0,
|
||||
pitch: pitch || 45,
|
||||
duration: duration || 2000
|
||||
},
|
||||
bubbles: true
|
||||
});
|
||||
|
||||
mapInstance.dispatchEvent(mapboxEvent);
|
||||
};
|
||||
|
||||
// Add event listener
|
||||
document.addEventListener('mapbox_fly_to', handleMapFlyTo as EventListener);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('mapbox_fly_to', handleMapFlyTo as EventListener);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Handle year-month timeline change
|
||||
const handleTimelineChange = useCallback((year: number, month: number, progress: number) => {
|
||||
setSelectedYear(year)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
"use client"
|
||||
|
||||
import type React from "react"
|
||||
import { useState, useCallback, useRef } from "react"
|
||||
import { useState, useCallback, useRef, useEffect } from "react"
|
||||
import { type ViewState, Map, type MapRef, NavigationControl, GeolocateControl } from "react-map-gl/mapbox"
|
||||
import { FullscreenControl } from "react-map-gl/mapbox"
|
||||
import { BASE_BEARING, BASE_LATITUDE, BASE_LONGITUDE, BASE_PITCH, BASE_ZOOM, MAPBOX_STYLES, type MapboxStyle } from "@/app/_utils/const/map"
|
||||
|
@ -79,6 +79,34 @@ export default function MapView({
|
|||
[onMoveEnd],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!mapRef.current) return;
|
||||
|
||||
const mapElement = mapRef.current.getMap().getContainer();
|
||||
|
||||
// Handle fly to event
|
||||
const handleMapFly = (e: CustomEvent) => {
|
||||
if (!e.detail || !mapRef.current) return;
|
||||
|
||||
const { center, zoom, bearing, pitch, duration } = e.detail;
|
||||
|
||||
mapRef.current.flyTo({
|
||||
center,
|
||||
zoom,
|
||||
bearing,
|
||||
pitch,
|
||||
duration,
|
||||
essential: true
|
||||
});
|
||||
};
|
||||
|
||||
mapElement.addEventListener('mapbox_fly', handleMapFly as EventListener);
|
||||
|
||||
return () => {
|
||||
mapElement.removeEventListener('mapbox_fly', handleMapFly as EventListener);
|
||||
};
|
||||
}, [mapRef.current]);
|
||||
|
||||
return (
|
||||
<div className={`relative ${className}`}>
|
||||
<div className="flex h-full">
|
||||
|
|
Loading…
Reference in New Issue