fix how to work , about,blog

This commit is contained in:
2026-05-29 18:02:58 +05:30
parent 0a65d2e04a
commit 88722329d5
30 changed files with 2881 additions and 866 deletions

View File

@@ -0,0 +1,103 @@
"use client";
import React, { useEffect } from "react";
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
/**
* AnimationProvider
* Initializes GSAP + ScrollTrigger globally, refreshes on route changes,
* and provides smooth defaults.
*/
export default function AnimationProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
gsap.registerPlugin(ScrollTrigger);
// Global GSAP defaults for buttery animations
gsap.defaults({
ease: "power3.out",
duration: 0.8,
});
const initDecorativeBlocks = () => {
// Clean up previous block triggers to avoid duplicates
ScrollTrigger.getAll().forEach((t) => {
if (t.vars && (t.vars as any).id === "block-deco") {
t.kill();
}
});
const decorativeBlocks = document.querySelectorAll(".block-decoration");
decorativeBlocks.forEach((block) => {
ScrollTrigger.create({
id: "block-deco",
trigger: block,
start: "top 92%",
onEnter: () => {
setTimeout(() => {
block.classList.add("animated");
}, 150);
},
onEnterBack: () => {
setTimeout(() => {
block.classList.add("animated");
}, 150);
},
onLeave: () => {
block.classList.remove("animated");
},
onLeaveBack: () => {
block.classList.remove("animated");
},
});
});
};
// Run initializations
initDecorativeBlocks();
// Refresh ScrollTrigger at staggered intervals to account for lazy-loaded assets/images over the network
const timeouts = [
setTimeout(() => {
initDecorativeBlocks();
ScrollTrigger.refresh(true);
}, 100),
setTimeout(() => {
initDecorativeBlocks();
ScrollTrigger.refresh(true);
}, 500),
setTimeout(() => {
initDecorativeBlocks();
ScrollTrigger.refresh(true);
}, 1500),
setTimeout(() => {
initDecorativeBlocks();
ScrollTrigger.refresh(true);
}, 3000),
];
// Refresh on full window load (when all images, styles, and fonts are in place)
const handleLoad = () => {
initDecorativeBlocks();
ScrollTrigger.refresh(true);
};
window.addEventListener("load", handleLoad);
// Also refresh on window resize
const handleResize = () => {
initDecorativeBlocks();
ScrollTrigger.refresh(true);
};
window.addEventListener("resize", handleResize);
return () => {
timeouts.forEach(clearTimeout);
window.removeEventListener("load", handleLoad);
window.removeEventListener("resize", handleResize);
ScrollTrigger.getAll().forEach((t) => t.kill());
};
}, []);
return <>{children}</>;
}

680
src/animations/Reveal.tsx Normal file
View File

@@ -0,0 +1,680 @@
"use client";
import React, { useEffect, useRef, useState, useCallback } from "react";
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
// Register ScrollTrigger safely
if (typeof window !== "undefined") {
gsap.registerPlugin(ScrollTrigger);
}
/* ============================================================
1. ScrollReveal
Fade-in + slide-up on scroll. The workhorse animation.
============================================================ */
interface ScrollRevealProps {
children: React.ReactNode;
delay?: number;
duration?: number;
yOffset?: number;
xOffset?: number;
className?: string;
triggerOnce?: boolean;
}
export function ScrollReveal({
children,
delay = 0,
duration = 0.8,
yOffset = 40,
xOffset = 0,
className = "",
triggerOnce = false,
}: ScrollRevealProps) {
const elementRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const el = elementRef.current;
if (!el) return;
gsap.set(el, { y: yOffset, x: xOffset, opacity: 0 });
const trigger = ScrollTrigger.create({
trigger: el,
start: "top 88%",
onEnter: (self) => {
gsap.to(el, {
y: 0,
x: 0,
opacity: 1,
duration,
ease: "power3.out",
delay,
overwrite: "auto",
});
if (triggerOnce) self.kill();
},
onEnterBack: () => {
if (!triggerOnce) {
gsap.to(el, {
y: 0,
x: 0,
opacity: 1,
duration,
ease: "power3.out",
delay,
overwrite: "auto",
});
}
},
onLeave: () => {
if (!triggerOnce) {
gsap.set(el, { y: yOffset, x: xOffset, opacity: 0 });
}
},
onLeaveBack: () => {
if (!triggerOnce) {
gsap.set(el, { y: yOffset, x: xOffset, opacity: 0 });
}
},
});
return () => trigger?.kill();
}, [delay, duration, yOffset, xOffset, triggerOnce]);
return (
<div ref={elementRef} className={className}>
{children}
</div>
);
}
/* ============================================================
2. RevealText
Splits text into words or characters and reveals with slide-up mask.
============================================================ */
interface RevealTextProps {
children: string;
type?: "words" | "chars";
delay?: number;
duration?: number;
className?: string;
triggerOnce?: boolean;
}
export function RevealText({
children,
type = "words",
delay = 0,
duration = 0.85,
className = "",
triggerOnce = false,
}: RevealTextProps) {
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const el = containerRef.current;
if (!el) return;
const items = el.querySelectorAll(".reveal-item");
if (!items.length) return;
gsap.set(items, { y: "110%", opacity: 0 });
const trigger = ScrollTrigger.create({
trigger: el,
start: "top 88%",
onEnter: (self) => {
gsap.to(items, {
y: "0%",
opacity: 1,
duration,
ease: "power4.out",
stagger: type === "chars" ? 0.02 : 0.04,
delay,
overwrite: "auto",
});
if (triggerOnce) self.kill();
},
onEnterBack: () => {
if (!triggerOnce) {
gsap.to(items, {
y: "0%",
opacity: 1,
duration,
ease: "power4.out",
stagger: type === "chars" ? 0.02 : 0.04,
delay,
overwrite: "auto",
});
}
},
onLeave: () => {
if (!triggerOnce) {
gsap.set(items, { y: "110%", opacity: 0 });
}
},
onLeaveBack: () => {
if (!triggerOnce) {
gsap.set(items, { y: "110%", opacity: 0 });
}
},
});
return () => trigger?.kill();
}, [children, type, delay, duration, triggerOnce]);
const renderContent = () => {
if (type === "chars") {
return children.split("").map((char, i) => (
<span key={i} className="inline-block overflow-hidden" style={{ height: "1.2em", lineHeight: "1.2em", verticalAlign: "middle" }}>
<span className="reveal-item inline-block">{char === " " ? "\u00A0" : char}</span>
</span>
));
}
return children.split(" ").map((word, i) => (
<span key={i} className="inline-block overflow-hidden" style={{ height: "1.2em", lineHeight: "1.2em", marginRight: "0.25em", verticalAlign: "middle" }}>
<span className="reveal-item inline-block">{word}</span>
</span>
));
};
return (
<div ref={containerRef} className={className} style={{ display: "flex", flexWrap: "wrap" }}>
{renderContent()}
</div>
);
}
/* ============================================================
3. Magnetic
Cursor-tracking magnetic pull on elements.
============================================================ */
interface MagneticProps {
children: React.ReactElement;
range?: number;
strength?: number;
}
export function Magnetic({ children, range = 45, strength = 0.35 }: MagneticProps) {
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const el = containerRef.current;
if (!el) return;
const child = el.firstElementChild as HTMLElement;
if (!child) return;
const handleMouseMove = (e: MouseEvent) => {
const rect = el.getBoundingClientRect();
const relX = e.clientX - (rect.left + rect.width / 2);
const relY = e.clientY - (rect.top + rect.height / 2);
const dist = Math.sqrt(relX * relX + relY * relY);
if (dist < range) {
gsap.to(child, { x: relX * strength, y: relY * strength, ease: "power2.out", duration: 0.4 });
} else {
gsap.to(child, { x: 0, y: 0, ease: "elastic.out(1.2, 0.4)", duration: 0.8 });
}
};
const handleMouseLeave = () => {
gsap.to(child, { x: 0, y: 0, ease: "elastic.out(1.2, 0.4)", duration: 0.8 });
};
el.addEventListener("mousemove", handleMouseMove);
el.addEventListener("mouseleave", handleMouseLeave);
return () => {
el.removeEventListener("mousemove", handleMouseMove);
el.removeEventListener("mouseleave", handleMouseLeave);
};
}, [range, strength]);
return (
<div ref={containerRef} className="inline-block">
{children}
</div>
);
}
/* ============================================================
4. ShimmerText
Premium shimmering gradient sweep on text.
============================================================ */
interface ShimmerTextProps {
children: string;
className?: string;
}
export function ShimmerText({ children, className = "" }: ShimmerTextProps) {
return (
<span
className={`inline-block bg-clip-text text-transparent bg-[linear-gradient(110deg,#ffffff,45%,#c01227,55%,#ffffff)] bg-[length:250%_100%] animate-[shimmer-sweep_6s_infinite_linear] ${className}`}
style={{ WebkitBackgroundClip: "text", backgroundClip: "text" }}
>
{children}
</span>
);
}
/* ============================================================
5. StaggerChildren
Wraps children and reveals them with staggered scroll animation.
============================================================ */
interface StaggerChildrenProps {
children: React.ReactNode;
stagger?: number;
duration?: number;
yOffset?: number;
className?: string;
triggerOnce?: boolean;
}
export function StaggerChildren({
children,
stagger = 0.1,
duration = 0.7,
yOffset = 35,
className = "",
triggerOnce = false,
}: StaggerChildrenProps) {
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const el = containerRef.current;
if (!el) return;
const items = el.children;
if (!items.length) return;
gsap.set(items, { y: yOffset, opacity: 0 });
const trigger = ScrollTrigger.create({
trigger: el,
start: "top 85%",
onEnter: (self) => {
gsap.to(items, {
y: 0,
opacity: 1,
duration,
ease: "power3.out",
stagger,
overwrite: "auto",
});
if (triggerOnce) self.kill();
},
onEnterBack: () => {
if (!triggerOnce) {
gsap.to(items, {
y: 0,
opacity: 1,
duration,
ease: "power3.out",
stagger,
overwrite: "auto",
});
}
},
onLeave: () => {
if (!triggerOnce) {
gsap.set(items, { y: yOffset, opacity: 0 });
}
},
onLeaveBack: () => {
if (!triggerOnce) {
gsap.set(items, { y: yOffset, opacity: 0 });
}
},
});
return () => trigger?.kill();
}, [stagger, duration, yOffset, triggerOnce]);
return (
<div ref={containerRef} className={className}>
{children}
</div>
);
}
/* ============================================================
6. ScaleReveal
Scales from 0.85 → 1.0 + fades in on scroll. Great for cards/images.
============================================================ */
interface ScaleRevealProps {
children: React.ReactNode;
delay?: number;
duration?: number;
className?: string;
triggerOnce?: boolean;
}
export function ScaleReveal({
children,
delay = 0,
duration = 0.8,
className = "",
triggerOnce = false,
}: ScaleRevealProps) {
const elementRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const el = elementRef.current;
if (!el) return;
gsap.set(el, { scale: 0.85, opacity: 0 });
const trigger = ScrollTrigger.create({
trigger: el,
start: "top 88%",
onEnter: (self) => {
gsap.to(el, {
scale: 1,
opacity: 1,
duration,
ease: "power3.out",
delay,
overwrite: "auto",
});
if (triggerOnce) self.kill();
},
onEnterBack: () => {
if (!triggerOnce) {
gsap.to(el, {
scale: 1,
opacity: 1,
duration,
ease: "power3.out",
delay,
overwrite: "auto",
});
}
},
onLeave: () => {
if (!triggerOnce) {
gsap.set(el, { scale: 0.85, opacity: 0 });
}
},
onLeaveBack: () => {
if (!triggerOnce) {
gsap.set(el, { scale: 0.85, opacity: 0 });
}
},
});
return () => trigger?.kill();
}, [delay, duration, triggerOnce]);
return (
<div ref={elementRef} className={className}>
{children}
</div>
);
}
/* ============================================================
7. SlideReveal
Slides from left or right with smooth reveal.
============================================================ */
interface SlideRevealProps {
children: React.ReactNode;
direction?: "left" | "right";
delay?: number;
duration?: number;
className?: string;
triggerOnce?: boolean;
}
export function SlideReveal({
children,
direction = "left",
delay = 0,
duration = 0.9,
className = "",
triggerOnce = false,
}: SlideRevealProps) {
const elementRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const el = elementRef.current;
if (!el) return;
const xStart = direction === "left" ? -60 : 60;
gsap.set(el, { x: xStart, opacity: 0 });
const trigger = ScrollTrigger.create({
trigger: el,
start: "top 88%",
onEnter: (self) => {
gsap.to(el, {
x: 0,
opacity: 1,
duration,
ease: "power3.out",
delay,
overwrite: "auto",
});
if (triggerOnce) self.kill();
},
onEnterBack: () => {
if (!triggerOnce) {
gsap.to(el, {
x: 0,
opacity: 1,
duration,
ease: "power3.out",
delay,
overwrite: "auto",
});
}
},
onLeave: () => {
if (!triggerOnce) {
gsap.set(el, { x: xStart, opacity: 0 });
}
},
onLeaveBack: () => {
if (!triggerOnce) {
gsap.set(el, { x: xStart, opacity: 0 });
}
},
});
return () => trigger?.kill();
}, [direction, delay, duration, triggerOnce]);
return (
<div ref={elementRef} className={className}>
{children}
</div>
);
}
/* ============================================================
8. ParallaxSection
Subtle parallax depth on scroll for images/backgrounds.
============================================================ */
interface ParallaxSectionProps {
children: React.ReactNode;
speed?: number;
className?: string;
}
export function ParallaxSection({
children,
speed = 0.15,
className = "",
}: ParallaxSectionProps) {
const elementRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const el = elementRef.current;
if (!el) return;
// Check for reduced motion preference
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
gsap.to(el, {
y: () => -ScrollTrigger.maxScroll(window) * speed * 0.1,
ease: "none",
scrollTrigger: {
trigger: el,
start: "top bottom",
end: "bottom top",
scrub: 1.5,
invalidateOnRefresh: true,
},
});
return () => {
ScrollTrigger.getAll().forEach((t) => {
if (t.trigger === el) t.kill();
});
};
}, [speed]);
return (
<div ref={elementRef} className={className} style={{ willChange: "transform" }}>
{children}
</div>
);
}
/* ============================================================
9. CountUp
Animated number counter that triggers on scroll.
============================================================ */
interface CountUpProps {
end: number;
start?: number;
duration?: number;
decimals?: number;
suffix?: string;
prefix?: string;
className?: string;
triggerOnce?: boolean;
}
export function CountUp({
end,
start = 0,
duration = 2,
decimals = 0,
suffix = "",
prefix = "",
className = "",
triggerOnce = false,
}: CountUpProps) {
const [value, setValue] = useState(start);
const elementRef = useRef<HTMLSpanElement>(null);
useEffect(() => {
const el = elementRef.current;
if (!el) return;
const trigger = ScrollTrigger.create({
trigger: el,
start: "top 90%",
onEnter: (self) => {
const obj = { val: start };
gsap.to(obj, {
val: end,
duration,
ease: "power2.out",
onUpdate: () => setValue(obj.val),
});
if (triggerOnce) self.kill();
},
onEnterBack: () => {
if (!triggerOnce) {
const obj = { val: start };
gsap.to(obj, {
val: end,
duration,
ease: "power2.out",
onUpdate: () => setValue(obj.val),
});
}
},
onLeave: () => {
if (!triggerOnce) {
setValue(start);
}
},
onLeaveBack: () => {
if (!triggerOnce) {
setValue(start);
}
},
});
return () => trigger?.kill();
}, [start, end, duration, triggerOnce]);
return (
<span ref={elementRef} className={className}>
{prefix}{value.toFixed(decimals)}{suffix}
</span>
);
}
/* ============================================================
10. Tilt3D
Subtle 3D perspective tilt following mouse on hover.
============================================================ */
interface Tilt3DProps {
children: React.ReactNode;
intensity?: number;
className?: string;
}
export function Tilt3D({
children,
intensity = 8,
className = "",
}: Tilt3DProps) {
const containerRef = useRef<HTMLDivElement>(null);
const handleMouseMove = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
const el = containerRef.current;
if (!el) return;
const rect = el.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width - 0.5;
const y = (e.clientY - rect.top) / rect.height - 0.5;
gsap.to(el, {
rotateY: x * intensity,
rotateX: -y * intensity,
duration: 0.5,
ease: "power2.out",
});
}, [intensity]);
const handleMouseLeave = useCallback(() => {
const el = containerRef.current;
if (!el) return;
gsap.to(el, {
rotateY: 0,
rotateX: 0,
duration: 0.8,
ease: "elastic.out(1, 0.5)",
});
}, []);
return (
<div style={{ perspective: "1000px" }}>
<div
ref={containerRef}
className={className}
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
style={{ transformStyle: "preserve-3d", willChange: "transform" }}
>
{children}
</div>
</div>
);
}