Files
webapp/app/composables/useMapboxFlyAnimation.ts
2026-01-07 09:10:35 +07:00

82 lines
1.9 KiB
TypeScript

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<void> => {
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<void>(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<void>(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
}
}