update
This commit is contained in:
37
frontEnd/components/shared/layout/animations/AppearDown.tsx
Normal file
37
frontEnd/components/shared/layout/animations/AppearDown.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
"use client";
|
||||
import { useEffect } from "react";
|
||||
import { gsap } from "gsap";
|
||||
import { ScrollTrigger } from "gsap/dist/ScrollTrigger";
|
||||
|
||||
const AppearDown = () => {
|
||||
useEffect(() => {
|
||||
if (window.innerWidth >= 992) {
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
const appearDownSections = document.querySelectorAll(".appear-down");
|
||||
appearDownSections.forEach((section) => {
|
||||
gsap.fromTo(
|
||||
section,
|
||||
{
|
||||
scale: 0.8,
|
||||
opacity: 0,
|
||||
},
|
||||
{
|
||||
scale: 1,
|
||||
opacity: 1,
|
||||
duration: 1.5,
|
||||
scrollTrigger: {
|
||||
trigger: section,
|
||||
scrub: 1,
|
||||
start: "top bottom",
|
||||
end: "bottom center",
|
||||
markers: false,
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
return null;
|
||||
};
|
||||
|
||||
export default AppearDown;
|
||||
@@ -0,0 +1,45 @@
|
||||
"use client";
|
||||
import { useEffect } from "react";
|
||||
|
||||
const ButtonHoverAnimation = () => {
|
||||
useEffect(() => {
|
||||
const btnAnim = document.querySelectorAll(".btn-anim");
|
||||
if (btnAnim.length > 0) {
|
||||
btnAnim.forEach((element) => {
|
||||
element.addEventListener("mouseenter", handleMouseEnter);
|
||||
element.addEventListener("mouseleave", handleMouseLeave);
|
||||
});
|
||||
|
||||
return () => {
|
||||
btnAnim.forEach((element) => {
|
||||
element.removeEventListener("mouseenter", handleMouseEnter);
|
||||
element.removeEventListener("mouseleave", handleMouseLeave);
|
||||
});
|
||||
};
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleMouseEnter = (e: any) => {
|
||||
const element = e.currentTarget as any;
|
||||
const span = element.querySelector("span");
|
||||
if (span) {
|
||||
const rect = element.getBoundingClientRect();
|
||||
span.style.left = `${e.clientX - rect.left}px`;
|
||||
span.style.top = `${e.clientY - rect.top}px`;
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseLeave = (e: any) => {
|
||||
const element = e.currentTarget as HTMLElement;
|
||||
const span = element.querySelector("span");
|
||||
if (span) {
|
||||
const rect = element.getBoundingClientRect();
|
||||
span.style.left = `${e.clientX - rect.left}px`;
|
||||
span.style.top = `${e.clientY - rect.top}px`;
|
||||
}
|
||||
};
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default ButtonHoverAnimation;
|
||||
122
frontEnd/components/shared/layout/animations/FadeAnimations.tsx
Normal file
122
frontEnd/components/shared/layout/animations/FadeAnimations.tsx
Normal file
@@ -0,0 +1,122 @@
|
||||
"use client";
|
||||
import { useEffect } from "react";
|
||||
import { gsap } from "gsap";
|
||||
import { ScrollTrigger } from "gsap/dist/ScrollTrigger";
|
||||
|
||||
const FadeAnimations = () => {
|
||||
useEffect(() => {
|
||||
if (window.innerWidth >= 992) {
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
const fadeWrapperRefs = document.querySelectorAll(".fade-wrapper");
|
||||
fadeWrapperRefs.forEach((fadeWrapperRef) => {
|
||||
const fadeItems = fadeWrapperRef.querySelectorAll(".fade-top");
|
||||
const fadeItemsBottom = fadeWrapperRef.querySelectorAll(".fade-bottom");
|
||||
const fadeItemsLeft = fadeWrapperRef.querySelectorAll(".fade-left");
|
||||
const fadeItemsRight = fadeWrapperRef.querySelectorAll(".fade-right");
|
||||
|
||||
// from top
|
||||
fadeItems.forEach((element, index) => {
|
||||
const delay = index * 0.15;
|
||||
gsap.set(element, {
|
||||
opacity: 0,
|
||||
y: 100,
|
||||
});
|
||||
|
||||
ScrollTrigger.create({
|
||||
trigger: element,
|
||||
start: "top 100%",
|
||||
end: "bottom 20%",
|
||||
scrub: 0.5,
|
||||
onEnter: () => {
|
||||
gsap.to(element, {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
duration: 1,
|
||||
delay: delay,
|
||||
});
|
||||
},
|
||||
once: true,
|
||||
});
|
||||
});
|
||||
|
||||
// from bottom
|
||||
fadeItemsBottom.forEach((element, index) => {
|
||||
const delay = index * 0.15;
|
||||
gsap.set(element, {
|
||||
opacity: 0,
|
||||
y: -100,
|
||||
});
|
||||
|
||||
ScrollTrigger.create({
|
||||
trigger: element,
|
||||
start: "top 100%",
|
||||
end: "bottom 20%",
|
||||
scrub: 0.5,
|
||||
onEnter: () => {
|
||||
gsap.to(element, {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
duration: 1,
|
||||
delay: delay,
|
||||
});
|
||||
},
|
||||
once: true,
|
||||
});
|
||||
});
|
||||
|
||||
// from left
|
||||
fadeItemsLeft.forEach((element, index) => {
|
||||
const delay = index * 0.15;
|
||||
gsap.set(element, {
|
||||
opacity: 0,
|
||||
x: 100,
|
||||
});
|
||||
|
||||
ScrollTrigger.create({
|
||||
trigger: element,
|
||||
start: "top 100%",
|
||||
end: "bottom 20%",
|
||||
scrub: 0.5,
|
||||
onEnter: () => {
|
||||
gsap.to(element, {
|
||||
opacity: 1,
|
||||
x: 0,
|
||||
duration: 1,
|
||||
delay: delay,
|
||||
});
|
||||
},
|
||||
once: true,
|
||||
});
|
||||
});
|
||||
|
||||
// from right
|
||||
fadeItemsRight.forEach((element, index) => {
|
||||
const delay = index * 0.15;
|
||||
gsap.set(element, {
|
||||
opacity: 0,
|
||||
x: -100,
|
||||
});
|
||||
|
||||
ScrollTrigger.create({
|
||||
trigger: element,
|
||||
start: "top 100%",
|
||||
end: "bottom 20%",
|
||||
scrub: 0.5,
|
||||
onEnter: () => {
|
||||
gsap.to(element, {
|
||||
opacity: 1,
|
||||
x: 0,
|
||||
duration: 1,
|
||||
delay: delay,
|
||||
});
|
||||
},
|
||||
once: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
return null;
|
||||
};
|
||||
|
||||
export default FadeAnimations;
|
||||
@@ -0,0 +1,38 @@
|
||||
"use client";
|
||||
import { useEffect } from "react";
|
||||
import { gsap } from "gsap";
|
||||
import { ScrollTrigger } from "gsap/dist/ScrollTrigger";
|
||||
|
||||
const FadeImageBottom = () => {
|
||||
useEffect(() => {
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
const deviceWidth = window.innerWidth;
|
||||
|
||||
if (
|
||||
document.querySelectorAll(".fade-img").length > 0 &&
|
||||
deviceWidth >= 992
|
||||
) {
|
||||
gsap.utils.toArray(".fade-img").forEach((el: any) => {
|
||||
const tl = gsap.timeline({
|
||||
scrollTrigger: {
|
||||
trigger: el,
|
||||
start: "center center",
|
||||
end: "+=40%",
|
||||
scrub: 1,
|
||||
pin: false,
|
||||
invalidateOnRefresh: true,
|
||||
},
|
||||
});
|
||||
|
||||
tl.to(el, {
|
||||
y: "120px",
|
||||
zIndex: "-1",
|
||||
duration: 1,
|
||||
});
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
return null;
|
||||
};
|
||||
|
||||
export default FadeImageBottom;
|
||||
@@ -0,0 +1,60 @@
|
||||
"use client";
|
||||
import { useEffect } from "react";
|
||||
import { gsap } from "gsap";
|
||||
import { ScrollTrigger } from "gsap/dist/ScrollTrigger";
|
||||
|
||||
const ParallaxImage = () => {
|
||||
useEffect(() => {
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
|
||||
const imageParallax = document.querySelectorAll(".parallax-image");
|
||||
|
||||
if (imageParallax.length > 0) {
|
||||
imageParallax.forEach((element) => {
|
||||
const animImageParallax = element as HTMLElement;
|
||||
const aipWrap = animImageParallax.closest(
|
||||
".parallax-image-wrap"
|
||||
) as HTMLElement;
|
||||
const aipInner = aipWrap?.querySelector(".parallax-image-inner");
|
||||
|
||||
if (aipWrap && aipInner) {
|
||||
let tl_ImageParallax = gsap.timeline({
|
||||
scrollTrigger: {
|
||||
trigger: aipWrap,
|
||||
start: "top bottom",
|
||||
end: "bottom top",
|
||||
scrub: true,
|
||||
},
|
||||
});
|
||||
|
||||
tl_ImageParallax.to(animImageParallax, {
|
||||
yPercent: 30,
|
||||
ease: "none",
|
||||
});
|
||||
gsap.fromTo(
|
||||
aipInner,
|
||||
{
|
||||
scale: 1.2,
|
||||
opacity: 0,
|
||||
},
|
||||
{
|
||||
scale: 1,
|
||||
opacity: 1,
|
||||
duration: 1.5,
|
||||
scrollTrigger: {
|
||||
trigger: aipWrap,
|
||||
start: "top 99%",
|
||||
markers: false,
|
||||
},
|
||||
}
|
||||
);
|
||||
ScrollTrigger.refresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default ParallaxImage;
|
||||
@@ -0,0 +1,39 @@
|
||||
"use client";
|
||||
import { useEffect } from "react";
|
||||
import { gsap } from "gsap";
|
||||
import { ScrollTrigger } from "gsap/dist/ScrollTrigger";
|
||||
import { ScrollToPlugin } from "gsap/dist/ScrollToPlugin";
|
||||
|
||||
const ScrollToElement = () => {
|
||||
useEffect(() => {
|
||||
gsap.registerPlugin(ScrollTrigger, ScrollToPlugin);
|
||||
const handleLinkClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
||||
e.preventDefault();
|
||||
const target = e.currentTarget.getAttribute("href");
|
||||
if (target) {
|
||||
gsap.to(window, {
|
||||
scrollTo: {
|
||||
y: target,
|
||||
offsetY: 200,
|
||||
},
|
||||
duration: 1.5,
|
||||
ease: "power3.inOut",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const links = document.querySelectorAll('a[href^="#"]');
|
||||
links.forEach((anchor: any) => {
|
||||
anchor.addEventListener("click", handleLinkClick);
|
||||
});
|
||||
|
||||
return () => {
|
||||
links.forEach((anchor: any) => {
|
||||
anchor.removeEventListener("click", handleLinkClick);
|
||||
});
|
||||
};
|
||||
}, []);
|
||||
return null;
|
||||
};
|
||||
|
||||
export default ScrollToElement;
|
||||
107
frontEnd/components/shared/layout/animations/SmoothScroll.tsx
Normal file
107
frontEnd/components/shared/layout/animations/SmoothScroll.tsx
Normal file
@@ -0,0 +1,107 @@
|
||||
"use client";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { gsap } from "gsap";
|
||||
import { ScrollTrigger } from "gsap/dist/ScrollTrigger";
|
||||
import Lenis from "lenis";
|
||||
import { usePathname } from "next/navigation";
|
||||
|
||||
const SmoothScroll = () => {
|
||||
const lenisRef = useRef<Lenis | null>(null);
|
||||
const pathname = usePathname();
|
||||
const [isNavigating, setIsNavigating] = useState(false);
|
||||
|
||||
// Handle pathname changes - PRIORITY 1
|
||||
useEffect(() => {
|
||||
setIsNavigating(true);
|
||||
|
||||
// Stop Lenis completely
|
||||
if (lenisRef.current) {
|
||||
lenisRef.current.stop();
|
||||
lenisRef.current.scrollTo(0, { immediate: true, force: true, lock: true });
|
||||
}
|
||||
|
||||
// Force scroll to top with all methods
|
||||
window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
|
||||
window.scrollTo(0, 0);
|
||||
document.documentElement.scrollTop = 0;
|
||||
document.body.scrollTop = 0;
|
||||
|
||||
// Keep forcing scroll for a brief period
|
||||
const forceScroll = () => {
|
||||
window.scrollTo(0, 0);
|
||||
document.documentElement.scrollTop = 0;
|
||||
document.body.scrollTop = 0;
|
||||
};
|
||||
|
||||
// Force scroll every 16ms (one frame) for 200ms
|
||||
const intervalId = setInterval(forceScroll, 16);
|
||||
|
||||
// After navigation is settled, restart Lenis
|
||||
const restartTimeout = setTimeout(() => {
|
||||
clearInterval(intervalId);
|
||||
|
||||
if (lenisRef.current) {
|
||||
lenisRef.current.scrollTo(0, { immediate: true, force: true });
|
||||
lenisRef.current.start();
|
||||
}
|
||||
|
||||
setIsNavigating(false);
|
||||
|
||||
// Final scroll enforcement
|
||||
window.scrollTo(0, 0);
|
||||
document.documentElement.scrollTop = 0;
|
||||
document.body.scrollTop = 0;
|
||||
}, 200);
|
||||
|
||||
return () => {
|
||||
clearInterval(intervalId);
|
||||
clearTimeout(restartTimeout);
|
||||
};
|
||||
}, [pathname]);
|
||||
|
||||
// Initialize Lenis - PRIORITY 2
|
||||
useEffect(() => {
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
|
||||
const lenis = new Lenis({
|
||||
duration: 1.2,
|
||||
easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
|
||||
orientation: 'vertical',
|
||||
gestureOrientation: 'vertical',
|
||||
smoothWheel: true,
|
||||
wheelMultiplier: 1,
|
||||
smoothTouch: false,
|
||||
touchMultiplier: 2,
|
||||
infinite: false,
|
||||
});
|
||||
|
||||
lenisRef.current = lenis;
|
||||
|
||||
// Force initial scroll to top
|
||||
lenis.scrollTo(0, { immediate: true, force: true });
|
||||
window.scrollTo(0, 0);
|
||||
|
||||
// Connect to GSAP ticker
|
||||
const tickerCallback = (time: number) => {
|
||||
if (!isNavigating) {
|
||||
lenis.raf(time * 350);
|
||||
}
|
||||
};
|
||||
|
||||
gsap.ticker.add(tickerCallback);
|
||||
gsap.ticker.lagSmoothing(0);
|
||||
|
||||
// Sync with ScrollTrigger
|
||||
lenis.on('scroll', ScrollTrigger.update);
|
||||
|
||||
return () => {
|
||||
lenis.destroy();
|
||||
gsap.ticker.remove(tickerCallback);
|
||||
lenisRef.current = null;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default SmoothScroll;
|
||||
@@ -0,0 +1,65 @@
|
||||
"use client";
|
||||
import { useEffect } from "react";
|
||||
import { gsap } from "gsap";
|
||||
import { ScrollTrigger } from "gsap/dist/ScrollTrigger";
|
||||
import SplitType from "split-type";
|
||||
|
||||
const SplitTextAnimations = () => {
|
||||
useEffect(() => {
|
||||
if (window.innerWidth >= 992) {
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
|
||||
new SplitType(".title-anim", {
|
||||
types: ["chars", "words"],
|
||||
});
|
||||
|
||||
const titleAnims = document.querySelectorAll(".title-anim");
|
||||
titleAnims.forEach((titleAnim) => {
|
||||
const charElements = titleAnim.querySelectorAll(".char");
|
||||
|
||||
charElements.forEach((char, index) => {
|
||||
const tl2 = gsap.timeline({
|
||||
scrollTrigger: {
|
||||
trigger: char,
|
||||
start: "top 90%",
|
||||
end: "bottom 60%",
|
||||
scrub: false,
|
||||
markers: false,
|
||||
toggleActions: "play none none none",
|
||||
},
|
||||
});
|
||||
|
||||
const charDelay = index * 0.03;
|
||||
|
||||
tl2.from(char, {
|
||||
duration: 0.8,
|
||||
x: 70,
|
||||
delay: charDelay,
|
||||
autoAlpha: 0,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const titleElements = document.querySelectorAll(".title-anim");
|
||||
|
||||
titleElements.forEach((el) => {
|
||||
const triggerEl = el as gsap.DOMTarget;
|
||||
gsap.to(triggerEl, {
|
||||
scrollTrigger: {
|
||||
trigger: triggerEl,
|
||||
start: "top 100%",
|
||||
markers: false,
|
||||
onEnter: () => {
|
||||
if (el instanceof Element) {
|
||||
el.classList.add("title-anim-active");
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
return null;
|
||||
};
|
||||
|
||||
export default SplitTextAnimations;
|
||||
@@ -0,0 +1,33 @@
|
||||
"use client";
|
||||
import VanillaTilt from "vanilla-tilt";
|
||||
|
||||
const VanillaTiltHover = () => {
|
||||
const tiltSelectors = [".btn-anim", ".topy-tilt"];
|
||||
const tiltElements = document.querySelectorAll(tiltSelectors.join(", "));
|
||||
|
||||
tiltElements.forEach((element) => {
|
||||
const tiltElement = element as HTMLElement;
|
||||
let tiltConfig: any = {
|
||||
speed: 3000,
|
||||
};
|
||||
|
||||
if (tiltElement.classList.contains("btn-anim")) {
|
||||
tiltConfig = {
|
||||
...tiltConfig,
|
||||
max: 15,
|
||||
perspective: 400,
|
||||
};
|
||||
} else if (tiltElement.classList.contains("topy-tilt")) {
|
||||
tiltConfig = {
|
||||
...tiltConfig,
|
||||
max: 5,
|
||||
};
|
||||
}
|
||||
|
||||
VanillaTilt.init(tiltElement, tiltConfig);
|
||||
});
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default VanillaTiltHover;
|
||||
Reference in New Issue
Block a user