MIF_E31221222/sigap-website/app/_components/map/fly-to.tsx

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
}