168 lines
5.3 KiB
TypeScript
168 lines
5.3 KiB
TypeScript
'use client'
|
|
|
|
import { motion, AnimatePresence } from 'framer-motion'
|
|
import { useState, useEffect } from 'react'
|
|
import Link from 'next/link'
|
|
import Image from 'next/image'
|
|
|
|
const heroSlots = [
|
|
{
|
|
srcs: [
|
|
'https://images.unsplash.com/photo-1582719478250-c89cae4dc85b?q=80&w=2070&auto=format&fit=crop',
|
|
'https://images.unsplash.com/photo-1566665797739-1674de7a421a?q=80&w=2070&auto=format&fit=crop',
|
|
'https://images.unsplash.com/photo-1590490360182-c33d57733427?q=80&w=2070&auto=format&fit=crop',
|
|
],
|
|
rotate: -12,
|
|
top: '15%',
|
|
left: '22%',
|
|
size: 'w-48 h-64 md:w-64 md:h-80',
|
|
delay: 0.2
|
|
},
|
|
{
|
|
srcs: [
|
|
'https://images.unsplash.com/photo-1544124499-58912cbddaad?q=80&w=2127&auto=format&fit=crop',
|
|
'https://images.unsplash.com/photo-1520250497591-112f2f40a3f4?q=80&w=2127&auto=format&fit=crop',
|
|
'https://images.unsplash.com/photo-1571896349842-33c89424de2d?q=80&w=2127&auto=format&fit=crop',
|
|
],
|
|
rotate: 10,
|
|
top: '10%',
|
|
left: '58%',
|
|
size: 'w-52 h-64 md:w-72 md:h-80',
|
|
delay: 0.4
|
|
},
|
|
{
|
|
srcs: [
|
|
'https://images.unsplash.com/photo-1507525428034-b723cf961d3e?q=80&w=2073&auto=format&fit=crop',
|
|
'https://images.unsplash.com/photo-1519046904884-53103b34b206?q=80&w=2073&auto=format&fit=crop',
|
|
'https://images.unsplash.com/photo-1506929199020-feee17651703?q=80&w=2073&auto=format&fit=crop',
|
|
],
|
|
rotate: -8,
|
|
top: '65%',
|
|
left: '18%',
|
|
size: 'w-48 h-60 md:w-64 md:h-72',
|
|
delay: 0.6
|
|
},
|
|
{
|
|
srcs: [
|
|
'https://images.unsplash.com/photo-1536935338218-422119932906?q=80&w=1964&auto=format&fit=crop',
|
|
'https://images.unsplash.com/photo-1618773928121-c32242e63f39?q=80&w=1964&auto=format&fit=crop',
|
|
'https://images.unsplash.com/photo-1560185007-cde436f6a4d0?q=80&w=1964&auto=format&fit=crop',
|
|
],
|
|
rotate: 15,
|
|
top: '72%',
|
|
left: '62%',
|
|
size: 'w-52 h-64 md:w-72 md:h-88',
|
|
delay: 0.8
|
|
}
|
|
]
|
|
|
|
function FloatingSlot({ slot, idx }: { slot: typeof heroSlots[0], idx: number }) {
|
|
const [currentIdx, setCurrentIdx] = useState(0)
|
|
|
|
useEffect(() => {
|
|
const interval = setInterval(() => {
|
|
setCurrentIdx((prev) => (prev + 1) % slot.srcs.length)
|
|
}, 4000 + idx * 500)
|
|
return () => clearInterval(interval)
|
|
}, [slot.srcs.length, idx])
|
|
|
|
return (
|
|
<motion.div
|
|
initial={{ opacity: 0, scale: 0.8, rotate: 0, y: 50 }}
|
|
animate={{
|
|
opacity: 1,
|
|
scale: 1,
|
|
rotate: slot.rotate,
|
|
y: 0
|
|
}}
|
|
transition={{
|
|
duration: 1.2,
|
|
delay: slot.delay,
|
|
ease: [0.33, 1, 0.68, 1]
|
|
}}
|
|
style={{
|
|
top: slot.top,
|
|
left: slot.left,
|
|
zIndex: 5
|
|
}}
|
|
className={`absolute ${slot.size} hidden md:block overflow-hidden`}
|
|
>
|
|
<AnimatePresence initial={false}>
|
|
<motion.div
|
|
key={currentIdx}
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
exit={{ opacity: 0 }}
|
|
transition={{ duration: 2, ease: "easeInOut" }}
|
|
className="absolute inset-0"
|
|
>
|
|
<Image
|
|
src={slot.srcs[currentIdx]}
|
|
alt="Luxury Experience"
|
|
fill
|
|
className="object-cover"
|
|
sizes="(max-width: 768px) 100vw, 33vw"
|
|
priority={idx < 2}
|
|
/>
|
|
</motion.div>
|
|
</AnimatePresence>
|
|
</motion.div>
|
|
)
|
|
}
|
|
|
|
export default function Hero({ lang, dict }: { lang: string, dict: any }) {
|
|
return (
|
|
<section className="sticky top-0 z-[20] h-screen w-full bg-[#FAF7F0] flex items-center justify-center overflow-hidden">
|
|
{/* BACKGROUND TEXT */}
|
|
<motion.div
|
|
initial={{ opacity: 0, scale: 0.95 }}
|
|
animate={{ opacity: 1, scale: 1 }}
|
|
transition={{ duration: 1.5, ease: [0.33, 1, 0.68, 1] }}
|
|
className="relative z-10 w-full text-center px-4"
|
|
>
|
|
<h1 className="text-[14vw] leading-[0.8] font-serif tracking-[-0.04em] text-[#1A1A1A] uppercase select-none drop-shadow-sm">
|
|
{dict.hero.title}
|
|
</h1>
|
|
</motion.div>
|
|
|
|
{/* FLOATING SLOTS */}
|
|
{heroSlots.map((slot, idx) => (
|
|
<FloatingSlot key={idx} slot={slot} idx={idx} />
|
|
))}
|
|
|
|
{/* BOTTOM CONTENT */}
|
|
<div className="absolute bottom-12 right-12 z-30 max-w-sm text-right flex flex-col items-end space-y-6">
|
|
<motion.p
|
|
initial={{ opacity: 0, x: 20 }}
|
|
animate={{ opacity: 1, x: 0 }}
|
|
transition={{ delay: 1.2, duration: 0.8 }}
|
|
className="text-[#1A1A1A]/70 text-[14px] leading-relaxed font-medium"
|
|
>
|
|
{dict.hero.desc}
|
|
</motion.p>
|
|
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 10 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ delay: 1.4, duration: 0.8 }}
|
|
>
|
|
<Link
|
|
href={`/${lang}/suites`}
|
|
className="inline-block px-8 py-4 bg-[#1A1A1A] text-white text-[12px] font-bold uppercase tracking-widest hover:bg-[#C88C4B] transition-colors duration-300 rounded-[2px] shadow-lg shadow-black/10"
|
|
>
|
|
{dict.hero.explore}
|
|
</Link>
|
|
</motion.div>
|
|
</div>
|
|
|
|
<motion.div
|
|
animate={{ y: [0, 10, 0] }}
|
|
transition={{ duration: 2, repeat: Infinity, ease: "easeInOut" }}
|
|
className="absolute bottom-8 left-1/2 -translate-x-1/2 opacity-30"
|
|
>
|
|
<div className="w-[1px] h-12 bg-[#1A1A1A]" />
|
|
</motion.div>
|
|
</section>
|
|
)
|
|
}
|