summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorYuren Hao <97327730+YurenHao0426@users.noreply.github.com>2025-02-02 23:59:29 -0600
committerGitHub <noreply@github.com>2025-02-02 23:59:29 -0600
commit1774317d667aed94b2a2f0acae885ce9420de8e2 (patch)
tree71ca71920e68a43536bc58085ebc96e193db776a /app
Add files via uploadHEADmain
initialization
Diffstat (limited to 'app')
-rw-r--r--app/components/WaveEffect.tsx71
-rw-r--r--app/globals.css23
-rw-r--r--app/layout.tsx23
-rw-r--r--app/page.tsx60
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>
+ )
+}
+