diff options
| author | Yuren Hao <97327730+YurenHao0426@users.noreply.github.com> | 2025-02-02 23:59:29 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-02-02 23:59:29 -0600 |
| commit | 1774317d667aed94b2a2f0acae885ce9420de8e2 (patch) | |
| tree | 71ca71920e68a43536bc58085ebc96e193db776a /app | |
initialization
Diffstat (limited to 'app')
| -rw-r--r-- | app/components/WaveEffect.tsx | 71 | ||||
| -rw-r--r-- | app/globals.css | 23 | ||||
| -rw-r--r-- | app/layout.tsx | 23 | ||||
| -rw-r--r-- | app/page.tsx | 60 |
4 files changed, 177 insertions, 0 deletions
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<WaveEffectProps> = ({ color, triggerWave, mousePosition }) => { + const canvasRef = useRef<HTMLCanvasElement>(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 <canvas ref={canvasRef} className="absolute top-0 left-0 w-full h-full pointer-events-none" /> +} + +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 ( + <html lang="en"> + <body className={inter.className}>{children}</body> + </html> + ) +} + 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<string | null>(null) + + const handleMouseEnter = useCallback((e: React.MouseEvent<HTMLButtonElement>) => { + 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 ( + <div className="relative min-h-screen flex flex-col items-center justify-center bg-gradient-to-r from-blue-500 to-purple-600 overflow-hidden"> + <WaveEffect + color={activeButton === "school" ? "173, 216, 230" : "230, 230, 250"} + triggerWave={triggerWave} + mousePosition={mousePosition} + /> + <h1 className="text-4xl md:text-6xl font-bold text-white mb-8 text-center z-10">🤓️👆</h1> + <div className="flex flex-col md:flex-row gap-4 z-10"> + <Link href="https://blackhao0426.web.illinois.edu" target="_blank" rel="noopener noreferrer"> + <button + className="px-8 py-4 bg-white text-blue-600 font-semibold rounded-lg shadow-md hover:bg-blue-100 transition duration-300 ease-in-out transform hover:scale-105" + onMouseEnter={(e) => { + handleMouseEnter(e) + setActiveButton("school") + }} + onMouseLeave={handleMouseLeave} + > + School Page + </button> + </Link> + <Link href="/archive"> + <button + className="px-8 py-4 bg-white text-purple-600 font-semibold rounded-lg shadow-md hover:bg-purple-100 transition duration-300 ease-in-out transform hover:scale-105" + onMouseEnter={(e) => { + handleMouseEnter(e) + setActiveButton("archive") + }} + onMouseLeave={handleMouseLeave} + > + Personal Archive + </button> + </Link> + </div> + </div> + ) +} + |
