summaryrefslogtreecommitdiff
path: root/components
diff options
context:
space:
mode:
authorlittleblack111 <littleblack11111@gmail.com>2024-10-13 00:38:37 +0800
committerlittleblack111 <littleblack11111@gmail.com>2024-10-13 00:38:37 +0800
commit433e08f2f798b4c04d57cd2d61b16a58aa5196be (patch)
tree699df47962e2fc921c9ddde8a034f41af92b3c63 /components
parent308eb96365a7cee0ebc8118673c3b6c85497ce3e (diff)
core: added home page
Diffstat (limited to 'components')
-rw-r--r--components/blob.tsx175
-rw-r--r--components/hacktype.tsx55
-rw-r--r--components/header.css123
-rw-r--r--components/header.tsx39
-rw-r--r--components/theme-provider.tsx9
5 files changed, 401 insertions, 0 deletions
diff --git a/components/blob.tsx b/components/blob.tsx
new file mode 100644
index 0000000..de80ad8
--- /dev/null
+++ b/components/blob.tsx
@@ -0,0 +1,175 @@
+"use client"
+import React, { useState, useEffect, useRef } from 'react';
+import isMobile from '@/app/lib/isMobile';
+
+// components/RandomBlob.tsx
+function getRandomInt(min: number, max: number): number {
+ min = Math.ceil(min);
+ max = Math.floor(max);
+ return Math.floor(Math.random() * (max - min + 1)) + min;
+}
+
+function getRandomColor(): string {
+ const letters = '0123456789ABCDEF';
+ let color = '#';
+ for (let i = 0; i < 6; i++) {
+ color += letters[Math.floor(Math.random() * 16)];
+ }
+ return color;
+}
+
+function generateGradient(): string {
+ let gradient = 'conic-gradient(';
+ let firstTimeInter = true;
+ const colorCount = Math.floor(Math.random() * 5) + 2;
+ for (let i = 0; i < colorCount; i++) {
+ if (firstTimeInter) {
+ gradient += getRandomColor();
+ firstTimeInter = false;
+ } else {
+ gradient += ', ' + getRandomColor();
+ }
+ }
+ gradient += ')';
+ return gradient;
+}
+
+const RandomBlob: React.FC = () => {
+ const blobRef = useRef<HTMLDivElement>(null);
+ const [isResetting, setIsResetting] = useState(false);
+ const [isFirstTime, setIsFirstTime] = useState(true);
+ const [mainInterval, setMainInterval] = useState<NodeJS.Timeout | null>(null);
+
+ const randomPosition = (element: HTMLElement) => {
+ const x = getRandomInt(0, window.innerWidth);
+ const y = getRandomInt(0, window.innerHeight);
+ element.style.position = 'absolute';
+ element.style.left = `${x}px`;
+ element.style.top = `${y}px`;
+ };
+
+ function setRandomBlob() {
+ if (blobRef.current) {
+ blobRef.current.style.width = `${getRandomInt(50, 100)}%`;
+ blobRef.current.style.height = `${getRandomInt(50, 100)}%`;
+ blobRef.current.style.transform = `rotate(${getRandomInt(0, 360)}deg)`;
+ }
+ };
+
+ function rrestblob() {
+ setRandomBlob();
+ blobRef.current!.style.background = generateGradient();
+ randomPosition(blobRef.current!);
+ const fadeInAnimation = blobRef.current!.animate([
+ { opacity: 0 },
+ { opacity: 0.4 }
+ ], {
+ duration: 3000,
+ iterations: 1,
+ fill: 'forwards'
+ });
+
+ fadeInAnimation?.addEventListener('finish', () => {
+ setIsResetting(false);
+ });
+ }
+
+ function resetBlob() {
+ if (blobRef.current) {
+ if (isResetting) {
+ return;
+ }
+ if (!isFirstTime) {
+ blobRef.current?.animate([
+ { opacity: 0.4 },
+ { opacity: 0, transform: 'scale(0.8)' }
+ ], {
+ duration: 3000,
+ iterations: 1,
+ fill: 'forwards'
+ }).addEventListener('finish', rrestblob);
+
+ setIsResetting(true);
+ } else {
+ rrestblob();
+ setIsFirstTime(false);
+ }
+ }
+ };
+
+ function main() {
+ resetBlob();
+ const interval = setTimeout(main, getRandomInt(7500, 30000));
+ setMainInterval(interval);
+ };
+
+ useEffect(() => {
+ main();
+ return () => {
+ if (mainInterval) {
+ clearTimeout(mainInterval);
+ }
+ };
+ }, []);
+
+ return (
+ <div
+ id="blob"
+ className="blob"
+ ref={blobRef}
+ onClick={() => {
+ if (isResetting) {
+ return;
+ }
+ setIsResetting(true);
+ if (mainInterval) {
+ clearTimeout(mainInterval);
+ }
+ main();
+ }}
+ />
+ );
+};
+
+function mouseBlob() {
+ // Implement the mouseBlob function
+ return <div>Mouse Blob</div>;
+}
+
+export default function Blob({ blobtype: initialBlobType }: { blobtype?: string }) {
+ const [blobtype, setBlobtype] = useState<string | undefined>(initialBlobType);
+ const mainIntervalRef = useRef<NodeJS.Timeout | null>(null);
+
+ useEffect(() => {
+ function main() {
+ if (mainIntervalRef.current) {
+ clearTimeout(mainIntervalRef.current);
+ }
+ mainIntervalRef.current = setTimeout(main, getRandomInt(7500, 30000));
+ }
+
+ main();
+
+ return () => {
+ if (mainIntervalRef.current) {
+ clearTimeout(mainIntervalRef.current);
+ }
+ };
+ }, []);
+
+ useEffect(() => {
+ if (blobtype === undefined) {
+ isMobile().then((isMobile) => {
+ setBlobtype(isMobile ? 'random' : 'mouse');
+ });
+ } else if (blobtype !== 'random' && blobtype !== 'mouse') {
+ throw new Error(`Invalid blob type ${blobtype}`);
+ }
+ }, [blobtype]);
+
+ if (blobtype === undefined) {
+ return null; // or a loading spinner
+ }
+
+ return blobtype === 'random' ? <RandomBlob /> : mouseBlob();
+} \ No newline at end of file
diff --git a/components/hacktype.tsx b/components/hacktype.tsx
new file mode 100644
index 0000000..59c91b3
--- /dev/null
+++ b/components/hacktype.tsx
@@ -0,0 +1,55 @@
+"use client"
+
+import { useEffect, useRef } from "react";
+
+const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+function hackType(element: HTMLElement) {
+ let iteration = 0;
+ const interval = setInterval(() => {
+ element.innerText = element.innerText
+ .split("")
+ .map((letter, index) => {
+ if (index < iteration) {
+ return element.getAttribute('data-value')![index];
+ }
+ return letters[Math.floor(Math.random() * 26)];
+ })
+ .join("");
+
+ if (iteration >= element.getAttribute('data-value')!.length) {
+ clearInterval(interval);
+ }
+
+ iteration += 1 / 5;
+ }, 20);
+}
+
+export default function HackType({ text }: { text: string }) {
+ const elementRef = useRef<HTMLDivElement>(null);
+
+ useEffect(() => {
+ if (elementRef.current) {
+ elementRef.current.setAttribute('data-value', text);
+ hackType(elementRef.current);
+
+ const interval = setInterval(() => {
+ hackType(elementRef.current!);
+ }, 10000);
+
+ document.addEventListener("visibilitychange", () => {
+ if (!document.hidden && elementRef.current) {
+ hackType(elementRef.current);
+ }
+ });
+
+ return () => clearInterval(interval);
+ }
+ }, [text]);
+
+ return (
+ <div ref={elementRef} className="hacktype">
+ {text}
+ </div>
+ );
+} \ No newline at end of file
diff --git a/components/header.css b/components/header.css
new file mode 100644
index 0000000..057ad90
--- /dev/null
+++ b/components/header.css
@@ -0,0 +1,123 @@
+header {
+ overflow: hidden;
+ position: fixed;
+ top: 2rem;
+ left: 0;
+ /* width: 75%; */
+ /* width: 100%; */
+ /* max-width: fit-content; */
+ /* margin-left: 12.5%; */
+ height: var(--topBarHeight);
+ /* justify-content: fit-content; */
+ align-items: center;
+ text-align: center;
+ /* background: color(0, 0, 0, .1); */
+ z-index: 1000;
+ /* background: rgba(0, 0, 0, 0.2); */
+ background-color: #000;
+ border-radius: 32px;
+ transition: all 0.1s;
+ -webkit-user-select: none; /* Safari */
+ -ms-user-select: none; /* IE 10 and IE 11 */
+ user-select: none; /* Standard syntax */
+}
+
+.navBar a {
+ font-family: var(--font-jetbrains);
+ position: relative;
+ height: 100%;
+ text-align: center;
+ font-size: 2rem;
+ font-weight: 500;
+ text-transform: capitalize;
+ text-align: center;
+ text-decoration: none;
+ margin-left: 10mm;
+ margin-right: 10mm;
+ padding-top: 5mm;
+ /* padding-bottom: 3mm; */
+ border-radius: 32px;
+ color: #F4E8C1;
+ transition: all .3s;
+ -webkit-user-select: none; /* Safari */
+ -ms-user-select: none; /* IE 10 and IE 11 */
+ user-select: none; /* Standard syntax */
+}
+
+.navBar .hleft a {float: left;}
+.navBar .hright a {float: right;}
+
+.navBar a::after {
+ content: '';
+ position: fixed;
+ left: 0;
+ bottom: 0;
+ width: 100%;
+ height: 1mm;
+ background: rgba(128, 128, 128, 0.2);
+ border-radius: 1mm;
+ transform: scaleX(0);
+ -webkit-transition: all 0.3s;
+ transition: all 0.3s;
+}
+
+.navBar a:hover::after {
+ transform: scaleX(1.1);
+}
+
+.navBar a:hover {
+ /* background: rgba(255, 255, 255, 0.2); */
+ /* outline: ridge rgba(128, 128, 128, .6); */
+ /* -webkit-backdrop-filter: blur(10px);
+ backdrop-filter: blur(10px); */
+ -webkit-transition: all 1s;
+ transition: all 1s
+}
+
+.navBar a.active::after {
+ content: '';
+ position: absolute;
+ left: 0;
+ bottom: 0;
+ width: 100%;
+ height: 1mm;
+ background: rgba(128, 128, 128, .9);
+ border-radius: 1mm;
+ transform: scaleX(1);
+}
+
+.navBar:active,
+.navBar:hover,
+header:hover,
+#navBarHeader:hover
+/* #navBarHeader:active */
+{
+ transform: scale(1.005);
+ /* -webkit-backdrop-filter: blur(10px);
+ backdrop-filter: blur(10px); */
+ transition: all 0.5s
+}
+
+header:active,
+#navBarHeader:active,
+.navBar:active {
+ transform: scale(1.05);
+ /* transition: transform 700ms; */
+}
+
+.navBar,
+header,
+#navBarHeader {
+ /* -webkit-backdrop-filter: blur(2.5px);
+ backdrop-filter: blur(2.5px); */
+ transition: all 0.5s
+}
+
+header.minimizedIsland {
+ opacity: 0;
+ scale: 0;
+ -webkit-animation-name: minimizeIsland;
+ animation-name: minimizeIsland;
+ animation-fill-mode: forwards;
+ transition: all 0.3s
+} \ No newline at end of file
diff --git a/components/header.tsx b/components/header.tsx
new file mode 100644
index 0000000..5b850e2
--- /dev/null
+++ b/components/header.tsx
@@ -0,0 +1,39 @@
+"use client"
+
+import Link from "next/link"
+import { usePathname } from "next/navigation"
+import { cn } from "@/lib/utils"
+import { useEffect, useState } from 'react';
+import "./header.css"
+
+
+export default function Header() {
+ const pathname = usePathname()
+ const [isMinimized, setIsMinimized] = useState(false);
+
+ useEffect(() => {
+ const handleScroll = () => {
+ setIsMinimized(window.scrollY > 100);
+ };
+
+ window.addEventListener('scroll', handleScroll);
+
+ return () => {
+ window.removeEventListener('scroll', handleScroll);
+ };
+ }, []);
+
+ return (
+ <header id="navBarHeader" className={isMinimized ? "minimizedIsland" : ""}>
+ <nav className="navBar">
+ <div className="hleft">
+ <Link id="btnHome" className={pathname === "/" ? "active": ""} style={{ textDecoration: 'unset' }} href="/">Home</Link>
+ </div>
+ <div className="hright">
+ <Link id="btnAbout" className={pathname === "/about" ? "active": ""} style={{ textDecoration: 'unset' }} href="/about">About</Link>
+ <Link id="btnGhRepo" className={pathname === "/ghrepos" ? "active": ""} style={{ textDecoration: 'unset' }} href="/ghrepos">Github Repos</Link>
+ </div>
+ </nav>
+ </header>
+ )
+} \ No newline at end of file
diff --git a/components/theme-provider.tsx b/components/theme-provider.tsx
new file mode 100644
index 0000000..6d17554
--- /dev/null
+++ b/components/theme-provider.tsx
@@ -0,0 +1,9 @@
+"use client"
+
+import * as React from "react"
+import { ThemeProvider as NextThemesProvider } from "next-themes"
+import { type ThemeProviderProps } from "next-themes/dist/types"
+
+export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
+ return <NextThemesProvider {...props}>{children}</NextThemesProvider>
+}