From 1774317d667aed94b2a2f0acae885ce9420de8e2 Mon Sep 17 00:00:00 2001 From: Yuren Hao <97327730+YurenHao0426@users.noreply.github.com> Date: Sun, 2 Feb 2025 23:59:29 -0600 Subject: Add files via upload initialization --- app/components/WaveEffect.tsx | 71 +++++++++++++++++++++++++++++++++++++++++++ app/globals.css | 23 ++++++++++++++ app/layout.tsx | 23 ++++++++++++++ app/page.tsx | 60 ++++++++++++++++++++++++++++++++++++ 4 files changed, 177 insertions(+) create mode 100644 app/components/WaveEffect.tsx create mode 100644 app/globals.css create mode 100644 app/layout.tsx create mode 100644 app/page.tsx (limited to 'app') diff --git a/app/components/WaveEffect.tsx b/app/components/WaveEffect.tsx new file mode 100644 index 0000000..853e25e --- /dev/null +++ b/app/components/WaveEffect.tsx @@ -0,0 +1,71 @@ +"use client" + +import type React from "react" +import { useRef, useEffect } from "react" + +interface WaveEffectProps { + color: string + triggerWave: boolean + mousePosition: { x: number; y: number } +} + +const WaveEffect: React.FC = ({ color, triggerWave, mousePosition }) => { + const canvasRef = useRef(null) + + useEffect(() => { + const canvas = canvasRef.current + if (!canvas) return + + const ctx = canvas.getContext("2d") + if (!ctx) return + + canvas.width = window.innerWidth + canvas.height = window.innerHeight + + let animationFrameId: number + const waves: { x: number; y: number; radius: number; opacity: number }[] = [] + + const createWave = (x: number, y: number) => { + waves.push({ x, y, radius: 0, opacity: 0.5 }) + } + + const drawWaves = () => { + ctx.clearRect(0, 0, canvas.width, canvas.height) + + waves.forEach((wave, index) => { + ctx.beginPath() + ctx.arc(wave.x, wave.y, wave.radius, 0, Math.PI * 2) + ctx.strokeStyle = `rgba(${color}, ${wave.opacity})` + ctx.lineWidth = 2 + ctx.stroke() + + // Slow down the expansion rate + wave.radius += 1 + + // Slow down the fade-out rate + wave.opacity -= 0.005 + + if (wave.opacity <= 0) { + waves.splice(index, 1) + } + }) + + animationFrameId = requestAnimationFrame(drawWaves) + } + + if (triggerWave) { + createWave(mousePosition.x, mousePosition.y) + } + + drawWaves() + + return () => { + cancelAnimationFrame(animationFrameId) + } + }, [color, triggerWave, mousePosition]) + + return +} + +export default WaveEffect + diff --git a/app/globals.css b/app/globals.css new file mode 100644 index 0000000..99d2c7a --- /dev/null +++ b/app/globals.css @@ -0,0 +1,23 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --foreground-rgb: 0, 0, 0; + --background-start-rgb: 214, 219, 220; + --background-end-rgb: 255, 255, 255; +} + +@media (prefers-color-scheme: dark) { + :root { + --foreground-rgb: 255, 255, 255; + --background-start-rgb: 0, 0, 0; + --background-end-rgb: 0, 0, 0; + } +} + +body { + color: rgb(var(--foreground-rgb)); + background: linear-gradient(to bottom, transparent, rgb(var(--background-end-rgb))) rgb(var(--background-start-rgb)); +} + diff --git a/app/layout.tsx b/app/layout.tsx new file mode 100644 index 0000000..f62b2be --- /dev/null +++ b/app/layout.tsx @@ -0,0 +1,23 @@ +import "./globals.css" +import { Inter } from "next/font/google" +import type React from "react" + +const inter = Inter({ subsets: ["latin"] }) + +export const metadata = { + title: "My Personal Blog & Portfolio", + description: "Explore my school work and personal archive", +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} + diff --git a/app/page.tsx b/app/page.tsx new file mode 100644 index 0000000..384f0bc --- /dev/null +++ b/app/page.tsx @@ -0,0 +1,60 @@ +"use client" + +import Link from "next/link" +import WaveEffect from "./components/WaveEffect" +import { useState, useCallback } from "react" + +export default function Home() { + const [triggerWave, setTriggerWave] = useState(false) + const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 }) + const [activeButton, setActiveButton] = useState(null) + + const handleMouseEnter = useCallback((e: React.MouseEvent) => { + const rect = e.currentTarget.getBoundingClientRect() + setMousePosition({ x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 }) + setTriggerWave(true) + }, []) + + const handleMouseLeave = useCallback(() => { + setTriggerWave(false) + // We're not resetting the activeButton here to keep the color consistent + }, []) + + return ( +
+ +

🤓️👆

+
+ + + + + + +
+
+ ) +} + -- cgit v1.2.3