feat: hero diagonal split with 3 dancer videos
- Three video panels with diagonal clip-paths and gold separator lines - Each video centered in its own column for clear visibility - Replaced nastya.mp4 with nastya-2.mp4 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,8 @@ import { FloatingHearts } from "@/components/ui/FloatingHearts";
|
|||||||
import { HeroLogo } from "@/components/ui/HeroLogo";
|
import { HeroLogo } from "@/components/ui/HeroLogo";
|
||||||
import type { SiteContent } from "@/types/content";
|
import type { SiteContent } from "@/types/content";
|
||||||
|
|
||||||
|
const VIDEOS = ["/video/ira.mp4", "/video/nadezda.mp4", "/video/nastya-2.mp4"];
|
||||||
|
|
||||||
interface HeroProps {
|
interface HeroProps {
|
||||||
data: SiteContent["hero"];
|
data: SiteContent["hero"];
|
||||||
}
|
}
|
||||||
@@ -15,10 +17,9 @@ export function Hero({ data: hero }: HeroProps) {
|
|||||||
const scrolledRef = useRef(false);
|
const scrolledRef = useRef(false);
|
||||||
|
|
||||||
const scrollToNext = useCallback(() => {
|
const scrollToNext = useCallback(() => {
|
||||||
const hero = sectionRef.current;
|
const el = sectionRef.current;
|
||||||
if (!hero) return;
|
if (!el) return;
|
||||||
// Find the next sibling section
|
let next = el.nextElementSibling;
|
||||||
let next = hero.nextElementSibling;
|
|
||||||
while (next && next.tagName !== "SECTION") {
|
while (next && next.tagName !== "SECTION") {
|
||||||
next = next.nextElementSibling;
|
next = next.nextElementSibling;
|
||||||
}
|
}
|
||||||
@@ -26,76 +27,82 @@ export function Hero({ data: hero }: HeroProps) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const hero = sectionRef.current;
|
const el = sectionRef.current;
|
||||||
if (!hero) return;
|
if (!el) return;
|
||||||
|
|
||||||
function handleWheel(e: WheelEvent) {
|
function handleWheel(e: WheelEvent) {
|
||||||
// Only trigger when scrolling down and still inside hero
|
|
||||||
if (e.deltaY <= 0 || scrolledRef.current) return;
|
if (e.deltaY <= 0 || scrolledRef.current) return;
|
||||||
if (window.scrollY > 10) return; // already scrolled past hero top
|
if (window.scrollY > 10) return;
|
||||||
|
|
||||||
scrolledRef.current = true;
|
scrolledRef.current = true;
|
||||||
scrollToNext();
|
scrollToNext();
|
||||||
|
|
||||||
// Reset after animation completes
|
|
||||||
setTimeout(() => { scrolledRef.current = false; }, 1000);
|
setTimeout(() => { scrolledRef.current = false; }, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTouchStart(e: TouchEvent) {
|
function handleTouchStart(e: TouchEvent) {
|
||||||
(hero as HTMLElement).dataset.touchY = String(e.touches[0].clientY);
|
(el as HTMLElement).dataset.touchY = String(e.touches[0].clientY);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTouchEnd(e: TouchEvent) {
|
function handleTouchEnd(e: TouchEvent) {
|
||||||
const startY = Number((hero as HTMLElement).dataset.touchY);
|
const startY = Number((el as HTMLElement).dataset.touchY);
|
||||||
const endY = e.changedTouches[0].clientY;
|
const endY = e.changedTouches[0].clientY;
|
||||||
const diff = startY - endY;
|
if (startY - endY > 50 && !scrolledRef.current && window.scrollY < 10) {
|
||||||
|
|
||||||
// Swipe down (finger moves up) with enough distance
|
|
||||||
if (diff > 50 && !scrolledRef.current && window.scrollY < 10) {
|
|
||||||
scrolledRef.current = true;
|
scrolledRef.current = true;
|
||||||
scrollToNext();
|
scrollToNext();
|
||||||
setTimeout(() => { scrolledRef.current = false; }, 1000);
|
setTimeout(() => { scrolledRef.current = false; }, 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hero.addEventListener("wheel", handleWheel, { passive: true });
|
el.addEventListener("wheel", handleWheel, { passive: true });
|
||||||
hero.addEventListener("touchstart", handleTouchStart, { passive: true });
|
el.addEventListener("touchstart", handleTouchStart, { passive: true });
|
||||||
hero.addEventListener("touchend", handleTouchEnd, { passive: true });
|
el.addEventListener("touchend", handleTouchEnd, { passive: true });
|
||||||
return () => {
|
return () => {
|
||||||
hero.removeEventListener("wheel", handleWheel);
|
el.removeEventListener("wheel", handleWheel);
|
||||||
hero.removeEventListener("touchstart", handleTouchStart);
|
el.removeEventListener("touchstart", handleTouchStart);
|
||||||
hero.removeEventListener("touchend", handleTouchEnd);
|
el.removeEventListener("touchend", handleTouchEnd);
|
||||||
};
|
};
|
||||||
}, [scrollToNext]);
|
}, [scrollToNext]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section ref={sectionRef} className="relative flex min-h-svh items-center justify-center overflow-hidden bg-[#050505]">
|
<section ref={sectionRef} className="relative flex min-h-svh items-center justify-center overflow-hidden bg-[#050505]">
|
||||||
{/* Animated gradient background */}
|
{/* Diagonal split background — 3 dancer videos */}
|
||||||
<div className="hero-bg-gradient absolute inset-0" />
|
<div className="absolute inset-0">
|
||||||
|
{VIDEOS.map((src, i) => {
|
||||||
{/* Glow orbs */}
|
const positions = [
|
||||||
|
{ left: "0%", width: "38%" },
|
||||||
|
{ left: "31%", width: "38%" },
|
||||||
|
{ left: "62%", width: "38%" },
|
||||||
|
];
|
||||||
|
const clips = [
|
||||||
|
"polygon(0 0, 100% 0, 86% 100%, 0 100%)",
|
||||||
|
"polygon(14% 0, 100% 0, 86% 100%, 0 100%)",
|
||||||
|
"polygon(14% 0, 100% 0, 100% 100%, 0 100%)",
|
||||||
|
];
|
||||||
|
return (
|
||||||
<div
|
<div
|
||||||
className="hero-glow-orb"
|
key={src}
|
||||||
|
className="absolute top-0 bottom-0 overflow-hidden"
|
||||||
style={{
|
style={{
|
||||||
width: "500px",
|
left: positions[i].left,
|
||||||
height: "500px",
|
width: positions[i].width,
|
||||||
top: "-10%",
|
clipPath: clips[i],
|
||||||
left: "50%",
|
|
||||||
transform: "translateX(-50%)",
|
|
||||||
background: "radial-gradient(circle, rgba(201, 169, 110, 0.12), transparent 70%)",
|
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
<div
|
<video
|
||||||
className="hero-glow-orb"
|
autoPlay muted loop playsInline
|
||||||
style={{
|
className="absolute inset-0 h-full w-full object-cover object-center"
|
||||||
width: "300px",
|
>
|
||||||
height: "300px",
|
<source src={src} type="video/mp4" />
|
||||||
bottom: "10%",
|
</video>
|
||||||
right: "10%",
|
<div className="absolute inset-0 bg-black/50" />
|
||||||
background: "radial-gradient(circle, rgba(201, 169, 110, 0.08), transparent 70%)",
|
</div>
|
||||||
animationDelay: "3s",
|
);
|
||||||
}}
|
})}
|
||||||
/>
|
{/* Gold diagonal lines between panels */}
|
||||||
|
<svg className="absolute inset-0 h-full w-full z-10 pointer-events-none" preserveAspectRatio="none" viewBox="0 0 100 100">
|
||||||
|
<line x1="38" y1="0" x2="33" y2="100" stroke="rgba(201,169,110,0.25)" strokeWidth="0.15" />
|
||||||
|
<line x1="69" y1="0" x2="64" y2="100" stroke="rgba(201,169,110,0.25)" strokeWidth="0.15" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Floating hearts */}
|
{/* Floating hearts */}
|
||||||
<FloatingHearts />
|
<FloatingHearts />
|
||||||
@@ -103,7 +110,6 @@ export function Hero({ data: hero }: HeroProps) {
|
|||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div className="section-container relative z-10 text-center">
|
<div className="section-container relative z-10 text-center">
|
||||||
<div className="hero-logo relative mx-auto mb-10 flex items-center justify-center" style={{ width: 220, height: 181 }}>
|
<div className="hero-logo relative mx-auto mb-10 flex items-center justify-center" style={{ width: 220, height: 181 }}>
|
||||||
{/* Soft ambient glow behind heart */}
|
|
||||||
<div className="absolute -inset-10 rounded-full blur-[80px]" style={{ background: "radial-gradient(circle, rgba(201,169,110,0.25), transparent 70%)" }} />
|
<div className="absolute -inset-10 rounded-full blur-[80px]" style={{ background: "radial-gradient(circle, rgba(201,169,110,0.25), transparent 70%)" }} />
|
||||||
<div className="hero-logo-heartbeat relative">
|
<div className="hero-logo-heartbeat relative">
|
||||||
<HeroLogo
|
<HeroLogo
|
||||||
@@ -127,7 +133,6 @@ export function Hero({ data: hero }: HeroProps) {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user