129 lines
4.6 KiB
TypeScript
129 lines
4.6 KiB
TypeScript
"use client"
|
|
|
|
import { IBaseLayerProps } from "@/app/_utils/types/map"
|
|
import { useEffect, useRef } from "react"
|
|
|
|
|
|
export default function FlyToHandler({ map }: Pick<IBaseLayerProps, "map">) {
|
|
// Track active animations
|
|
const animationRef = useRef<number | null>(null)
|
|
|
|
useEffect(() => {
|
|
if (!map) return
|
|
|
|
const handleFlyToEvent = (e: CustomEvent) => {
|
|
if (!map || !e.detail) return
|
|
|
|
const { longitude, latitude, zoom, bearing, pitch, duration } = e.detail
|
|
|
|
map.flyTo({
|
|
center: [longitude, latitude],
|
|
zoom: zoom || 15,
|
|
bearing: bearing || 0,
|
|
pitch: pitch || 45,
|
|
duration: duration || 2000,
|
|
})
|
|
|
|
// Cancel any existing animation
|
|
if (animationRef.current) {
|
|
cancelAnimationFrame(animationRef.current)
|
|
}
|
|
|
|
// Add a highlight or pulse effect to the target incident
|
|
try {
|
|
if (map.getLayer("target-incident-highlight")) {
|
|
map.removeLayer("target-incident-highlight")
|
|
}
|
|
|
|
if (map.getSource("target-incident-highlight")) {
|
|
map.removeSource("target-incident-highlight")
|
|
}
|
|
|
|
map.addSource("target-incident-highlight", {
|
|
type: "geojson",
|
|
data: {
|
|
type: "Feature",
|
|
geometry: {
|
|
type: "Point",
|
|
coordinates: [longitude, latitude],
|
|
},
|
|
properties: {},
|
|
},
|
|
})
|
|
|
|
map.addLayer({
|
|
id: "target-incident-highlight",
|
|
source: "target-incident-highlight",
|
|
type: "circle",
|
|
paint: {
|
|
"circle-radius": ["interpolate", ["linear"], ["zoom"], 10, 10, 15, 15, 20, 20],
|
|
"circle-color": "#ff0000",
|
|
"circle-opacity": 0.7,
|
|
"circle-stroke-width": 2,
|
|
"circle-stroke-color": "#ffffff",
|
|
},
|
|
})
|
|
|
|
// Add a slower pulsing effect
|
|
let size = 10
|
|
let frameCount = 0
|
|
const animationSpeed = 3; // Higher value = slower animation (skip frames)
|
|
|
|
const animatePulse = () => {
|
|
if (!map || !map.getLayer("target-incident-highlight")) {
|
|
if (animationRef.current) {
|
|
cancelAnimationFrame(animationRef.current)
|
|
animationRef.current = null
|
|
}
|
|
return
|
|
}
|
|
|
|
frameCount++;
|
|
// Only update size every few frames to slow down the animation
|
|
if (frameCount % animationSpeed === 0) {
|
|
size = (size % 20) + 0.5; // Smaller increment for smoother, slower animation
|
|
}
|
|
|
|
map.setPaintProperty("target-incident-highlight", "circle-radius", [
|
|
"interpolate",
|
|
["linear"],
|
|
["zoom"],
|
|
10,
|
|
size,
|
|
15,
|
|
size * 1.5,
|
|
20,
|
|
size * 2,
|
|
])
|
|
|
|
animationRef.current = requestAnimationFrame(animatePulse)
|
|
}
|
|
|
|
animationRef.current = requestAnimationFrame(animatePulse)
|
|
} catch (error) {
|
|
console.error("Error adding highlight effect:", error)
|
|
}
|
|
}
|
|
|
|
// Listen for the custom fly-to event
|
|
map.getCanvas().addEventListener("mapbox_fly_to", handleFlyToEvent as EventListener)
|
|
// Also listen on the document to ensure we catch the event
|
|
document.addEventListener("mapbox_fly_to", handleFlyToEvent as EventListener)
|
|
|
|
return () => {
|
|
if (map && map.getCanvas()) {
|
|
map.getCanvas().removeEventListener("mapbox_fly_to", handleFlyToEvent as EventListener)
|
|
}
|
|
document.removeEventListener("mapbox_fly_to", handleFlyToEvent as EventListener)
|
|
|
|
// Clean up animation on unmount
|
|
if (animationRef.current) {
|
|
cancelAnimationFrame(animationRef.current)
|
|
animationRef.current = null
|
|
}
|
|
}
|
|
}, [map])
|
|
|
|
return null
|
|
}
|