diff --git a/app/globals.css b/app/globals.css index cb102de..50d606f 100644 --- a/app/globals.css +++ b/app/globals.css @@ -50,4 +50,13 @@ body, body { @apply bg-background text-foreground; } +} + +.circle { + @apply absolute w-7 h-7 rounded-full pointer-events-none transition-transform duration-100; + background: radial-gradient(circle, rgba(0, 62, 208, 0.5), rgba(219, 251, 255, 0.5)); +} + +.circle-container { + @apply fixed top-0 left-0 w-full h-full pointer-events-none z-[9999]; } \ No newline at end of file diff --git a/app/layout.tsx b/app/layout.tsx index ac5c1ce..496d1b9 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -7,9 +7,9 @@ import ToastProvider from '@/providers/toast-provider' import Navbar from '@/components/navbar' // import HomePage from './(routes)/page' import Footer from '@/components/footer' -import AnimatedCursor from 'react-animated-cursor' import './globals.css' import BackToTop from '@/components/ui/BacktoTop' +import CursorTrail from '@/components/CursorTrail' const font = Urbanist({ subsets: ['latin'] }) @@ -29,30 +29,8 @@ export default function RootLayout({ - - + diff --git a/components/CursorTrail.tsx b/components/CursorTrail.tsx new file mode 100644 index 0000000..c835586 --- /dev/null +++ b/components/CursorTrail.tsx @@ -0,0 +1,82 @@ +"use client"; +import React, { useEffect } from 'react'; + +interface ExtendedHTMLElement extends HTMLElement { + x?: number; + y?: number; +} + +const CursorTrail: React.FC = () => { + useEffect(() => { + const circles: ExtendedHTMLElement[] = []; + const numCircles = 25; + + + for (let i = 0; i < numCircles; i++) { + circles.push({ x: 0, y: 0, size: 25 }); + } + + let mouseX = 0; + let mouseY = 0; + let isMouseMoving = false; + + const handleMouseMove = (e: MouseEvent) => { + mouseX = e.pageX; + mouseY = e.pageY - window.scrollY; + isMouseMoving = true; + }; + + const updateCircles = () => { + circles.forEach((circle, index) => { + const targetX = index === 0 ? mouseX : circles[index - 1].x; + const targetY = index === 0 ? mouseY : circles[index - 1].y; + + // for faster movement + circle.x += (targetX - circle.x) * 0.6; + circle.y += (targetY - circle.y) * 0.6; + + + circle.size = 25 - (index * 0.8); // the rate of size decrease + + const circleElement = document.querySelector(`#circle-${index}`) as ExtendedHTMLElement; + if (circleElement) { + circleElement.style.left = `${circle.x}px`; + circleElement.style.top = `${circle.y}px`; + circleElement.style.width = `${circle.size}px`; // width for circle effect + circleElement.style.height = `${circle.size}px`; // height for circle effect + } + }); + + if (!isMouseMoving) { + circles.forEach(circle => { + circle.x += (mouseX - circle.x) * 0.1; + circle.y += (mouseY - circle.y) * 0.1; + }); + } + + requestAnimationFrame(updateCircles); + }; + + window.addEventListener("mousemove", handleMouseMove); + updateCircles(); + + return () => { + window.removeEventListener("mousemove", handleMouseMove); + }; + }, []); + + return ( +
+ {Array.from({ length: 25 }).map((_, index) => ( +
+ ))} +
+ ); +}; + +export default CursorTrail;