import type { Map as MapboxMap } from 'mapbox-gl' interface FlyThroughSpaceOptions { targetCenter: [number, number] targetZoom?: number totalDuration?: number minZoom?: number // Minimum zoom (higher altitude) } export const useMapboxFlyAnimation = () => { const isAnimating = ref(false) const flyThroughSpace = async ( map: MapboxMap, options: FlyThroughSpaceOptions ): Promise => { const { targetCenter, targetZoom = 12, totalDuration = 5000, minZoom = 3 } = options // Stop any ongoing animation if (isAnimating.value) { map.stop() } isAnimating.value = true const currentCenter = map.getCenter() // Phase 1: lift-off (35% of time) const phase1Duration = totalDuration * 0.35 await new Promise(resolve => { map.easeTo({ center: [ (currentCenter.lng + targetCenter[0]) / 2, (currentCenter.lat + targetCenter[1]) / 2 ], zoom: minZoom, duration: phase1Duration, easing: (t) => t * (2 - t) // ease out }) setTimeout(resolve, phase1Duration) }) // Phase 2: move + descent (65% of time) const phase2Duration = totalDuration * 0.65 await new Promise(resolve => { map.easeTo({ center: targetCenter, zoom: targetZoom, duration: phase2Duration, easing: (t) => t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2 // ease in-out }) setTimeout(resolve, phase2Duration) }) isAnimating.value = false } const setupGlobeAtmosphere = (map: MapboxMap) => { map.setFog({ color: 'rgb(186, 210, 235)', 'high-color': 'rgb(36, 92, 223)', 'horizon-blend': 0.02, 'space-color': 'rgb(11, 11, 25)', 'star-intensity': 0.6 }) } return { flyThroughSpace, setupGlobeAtmosphere, isAnimating } }