"use client"
import { motion, useReducedMotion } from "motion/react"
import { useEffect, useMemo, useState, memo } from "react"
import { useOnboarding } from "./onboarding-context"
interface OrbProps {
size: number
initialX: number
initialY: number
duration: number
delay: number
revealDelay: number
shouldReveal: boolean
color: {
primary: string
secondary: string
tertiary: string
}
}
function FloatingOrb({
size,
initialX,
initialY,
duration,
delay,
revealDelay,
shouldReveal,
color,
}: OrbProps) {
const blurPixels = Math.min(64, Math.max(24, Math.floor(size * 0.08)))
const gradient = useMemo(() => {
return `radial-gradient(circle, ${color.primary} 0%, ${color.secondary} 40%, ${color.tertiary} 70%, transparent 100%)`
}, [color.primary, color.secondary, color.tertiary])
const style = useMemo(() => {
return {
width: size,
height: size,
background: gradient,
filter: `blur(${blurPixels}px)`,
willChange: "transform, opacity",
mixBlendMode: "plus-lighter",
} as any
}, [size, gradient, blurPixels])
const initial = useMemo(() => {
return {
x: initialX,
y: initialY,
scale: 0,
opacity: 0,
}
}, [initialX, initialY])
const animate = useMemo(() => {
if (!shouldReveal) {
return {
x: initialX,
y: initialY,
scale: 0,
opacity: 0,
}
}
return {
x: [initialX, initialX + 200, initialX - 150, initialX + 100, initialX],
y: [initialY, initialY - 180, initialY + 120, initialY - 80, initialY],
scale: [0.8, 1.2, 0.9, 1.1, 0.8],
opacity: 0.7,
}
}, [shouldReveal, initialX, initialY])
const transition = useMemo(() => {
return {
x: {
duration: shouldReveal ? duration : 0,
repeat: shouldReveal ? Number.POSITIVE_INFINITY : 0,
ease: [0.42, 0, 0.58, 1],
delay: shouldReveal ? delay + revealDelay : 0,
},
y: {
duration: shouldReveal ? duration : 0,
repeat: shouldReveal ? Number.POSITIVE_INFINITY : 0,
ease: [0.42, 0, 0.58, 1],
delay: shouldReveal ? delay + revealDelay : 0,
},
scale: {
duration: shouldReveal ? duration : 0.8,
repeat: shouldReveal ? Number.POSITIVE_INFINITY : 0,
ease: shouldReveal ? [0.42, 0, 0.58, 1] : [0, 0, 0.58, 1],
delay: shouldReveal ? delay + revealDelay : revealDelay,
},
opacity: {
duration: 1.2,
ease: [0, 0, 0.58, 1],
delay: shouldReveal ? revealDelay : 0,
},
} as any
}, [shouldReveal, duration, delay, revealDelay])
return (
)
}
const MemoFloatingOrb = memo(FloatingOrb)
export function FloatingOrbs() {
const { orbsRevealed } = useOnboarding()
const reduceMotion = useReducedMotion()
const [mounted, setMounted] = useState(false)
const [orbs, setOrbs] = useState<
Array<{
id: number
size: number
initialX: number
initialY: number
duration: number
delay: number
revealDelay: number
color: {
primary: string
secondary: string
tertiary: string
}
}>
>([])
useEffect(() => {
setMounted(true)
const screenWidth = typeof window !== "undefined" ? window.innerWidth : 1200
const screenHeight =
typeof window !== "undefined" ? window.innerHeight : 800
// Define edge zones (avoiding center)
const edgeThickness = Math.min(screenWidth, screenHeight) * 0.25 // 25% of smaller dimension
// Define rainbow color palette
const colorPalette = [
{
// Magenta
primary: "rgba(255, 0, 150, 0.6)",
secondary: "rgba(255, 100, 200, 0.4)",
tertiary: "rgba(255, 150, 220, 0.1)",
},
{
// Yellow
primary: "rgba(255, 235, 59, 0.6)",
secondary: "rgba(255, 245, 120, 0.4)",
tertiary: "rgba(255, 250, 180, 0.1)",
},
{
// Light Blue
primary: "rgba(100, 181, 246, 0.6)",
secondary: "rgba(144, 202, 249, 0.4)",
tertiary: "rgba(187, 222, 251, 0.1)",
},
{
// Orange (keeping original)
primary: "rgba(255, 154, 0, 0.6)",
secondary: "rgba(255, 206, 84, 0.4)",
tertiary: "rgba(255, 154, 0, 0.1)",
},
{
// Very Light Red/Pink
primary: "rgba(255, 138, 128, 0.6)",
secondary: "rgba(255, 171, 145, 0.4)",
tertiary: "rgba(255, 205, 210, 0.1)",
},
]
// Generate orb configurations positioned along edges
const newOrbs = Array.from({ length: 8 }, (_, i) => {
let x: number
let y: number
const zone = i % 4 // Rotate through 4 zones: top, right, bottom, left
switch (zone) {
case 0: // Top edge
x = Math.random() * screenWidth
y = Math.random() * edgeThickness
break
case 1: // Right edge
x = screenWidth - edgeThickness + Math.random() * edgeThickness
y = Math.random() * screenHeight
break
case 2: // Bottom edge
x = Math.random() * screenWidth
y = screenHeight - edgeThickness + Math.random() * edgeThickness
break
case 3: // Left edge
x = Math.random() * edgeThickness
y = Math.random() * screenHeight
break
default:
x = Math.random() * screenWidth
y = Math.random() * screenHeight
}
return {
id: i,
size: Math.random() * 300 + 200, // 200px to 500px
initialX: x,
initialY: y,
duration: Math.random() * 20 + 15, // 15-35 seconds (longer for more gentle movement)
delay: i * 0.4, // Staggered start for floating animation
revealDelay: i * 0.2, // Faster staggered reveal
color: colorPalette[i % colorPalette.length]!, // Cycle through rainbow colors
}
})
setOrbs(newOrbs)
}, [])
if (!mounted || orbs.length === 0) return null
return (
{orbs.map((orb) => (
))}
)
}