feat: Apple-style scroll-driven landing page
- Add ScrollAnimations component (ScrollReveal, ParallaxLayer, FadeOnScroll) - Add CinematicShowcase for scroll-driven product reveals - Update Tuxedo page with cinematic product showcase and parallax layers - Update main HeroSection with same premium scroll animations - Add scroll progress bar, staggered reveals, counter animations - Apply consistent Apple-style UX across landing and Tuxedo pages Features: - Sections pin and reveal content progressively on scroll - Parallax depth effects on decorative elements - Counter animations triggered by scroll - Smooth hover states and transitions - GSAP + Framer Motion for smooth animations
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -69,6 +69,7 @@ interface ScrollRevealProps {
|
||||
className?: string;
|
||||
stagger?: boolean;
|
||||
direction?: "up" | "down" | "left" | "right" | "scale";
|
||||
from?: "up" | "down" | "left" | "right" | "scale";
|
||||
delay?: number;
|
||||
}
|
||||
|
||||
@@ -77,9 +78,11 @@ export function ScrollReveal({
|
||||
className = "",
|
||||
stagger = false,
|
||||
direction = "up",
|
||||
from,
|
||||
delay = 0,
|
||||
}: ScrollRevealProps) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const effectiveDirection = from ?? direction;
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === "undefined" || !containerRef.current) return;
|
||||
@@ -111,7 +114,7 @@ export function ScrollReveal({
|
||||
// Staggered reveal
|
||||
const animProps: gsap.TweenVars = { opacity: 0 };
|
||||
|
||||
switch (direction) {
|
||||
switch (effectiveDirection) {
|
||||
case "up":
|
||||
animProps.y = 60;
|
||||
break;
|
||||
@@ -150,7 +153,7 @@ export function ScrollReveal({
|
||||
}, containerRef);
|
||||
|
||||
return () => ctx.revert();
|
||||
}, [direction, stagger, delay]);
|
||||
}, [effectiveDirection, stagger, delay]);
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className={className}>
|
||||
@@ -166,12 +169,14 @@ interface ParallaxLayerProps {
|
||||
children: React.ReactNode;
|
||||
speed?: number; // 0-1, higher = more movement
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
export function ParallaxLayer({
|
||||
children,
|
||||
speed = 0.5,
|
||||
className = "",
|
||||
style,
|
||||
}: ParallaxLayerProps) {
|
||||
const layerRef = useRef<HTMLDivElement>(null);
|
||||
const [isClient, setIsClient] = useState(false);
|
||||
@@ -202,11 +207,11 @@ export function ParallaxLayer({
|
||||
}, [isClient, speed]);
|
||||
|
||||
if (!isClient) {
|
||||
return <div className={className}>{children}</div>;
|
||||
return <div className={className} style={style}>{children}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={layerRef} className={className}>
|
||||
<div ref={layerRef} className={className} style={style}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
@@ -271,17 +276,21 @@ export function ProgressIndicator({
|
||||
interface FadeOnScrollProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
from?: "top" | "bottom" | "left" | "right";
|
||||
from?: "top" | "bottom" | "left" | "right" | "up" | "down";
|
||||
to?: "top" | "bottom" | "left" | "right";
|
||||
distance?: number;
|
||||
duration?: number;
|
||||
delay?: number;
|
||||
}
|
||||
|
||||
export function FadeOnScroll({
|
||||
children,
|
||||
className = "",
|
||||
from = "bottom",
|
||||
to,
|
||||
distance = 100,
|
||||
duration = 1,
|
||||
delay = 0,
|
||||
}: FadeOnScrollProps) {
|
||||
const elementRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@@ -293,6 +302,7 @@ export function FadeOnScroll({
|
||||
let fromY = 0;
|
||||
let fromX = 0;
|
||||
|
||||
// Use 'from' direction for the starting position
|
||||
switch (from) {
|
||||
case "top":
|
||||
fromY = distance;
|
||||
@@ -320,6 +330,7 @@ export function FadeOnScroll({
|
||||
y: 0,
|
||||
duration,
|
||||
ease: "power3.out",
|
||||
delay,
|
||||
scrollTrigger: {
|
||||
trigger: elementRef.current,
|
||||
start: "top 85%",
|
||||
@@ -330,7 +341,7 @@ export function FadeOnScroll({
|
||||
}, elementRef);
|
||||
|
||||
return () => ctx.revert();
|
||||
}, [from, distance, duration]);
|
||||
}, [from, distance, duration, delay]);
|
||||
|
||||
return (
|
||||
<div ref={elementRef} className={className}>
|
||||
|
||||
Reference in New Issue
Block a user