This commit is contained in:
46
package-lock.json
generated
46
package-lock.json
generated
@@ -8,6 +8,7 @@
|
|||||||
"name": "homepage",
|
"name": "homepage",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"framer-motion": "^12.23.6",
|
||||||
"next": "15.4.2",
|
"next": "15.4.2",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
"react-dom": "19.1.0"
|
"react-dom": "19.1.0"
|
||||||
@@ -22,6 +23,9 @@
|
|||||||
"eslint-config-next": "15.4.2",
|
"eslint-config-next": "15.4.2",
|
||||||
"tailwindcss": "^4",
|
"tailwindcss": "^4",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@alloc/quick-lru": {
|
"node_modules/@alloc/quick-lru": {
|
||||||
@@ -3326,6 +3330,33 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/framer-motion": {
|
||||||
|
"version": "12.23.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.6.tgz",
|
||||||
|
"integrity": "sha512-dsJ389QImVE3lQvM8Mnk99/j8tiZDM/7706PCqvkQ8sSCnpmWxsgX+g0lj7r5OBVL0U36pIecCTBoIWcM2RuKw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"motion-dom": "^12.23.6",
|
||||||
|
"motion-utils": "^12.23.6",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@emotion/is-prop-valid": "*",
|
||||||
|
"react": "^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@emotion/is-prop-valid": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/function-bind": {
|
"node_modules/function-bind": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||||
@@ -4598,6 +4629,21 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/motion-dom": {
|
||||||
|
"version": "12.23.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.6.tgz",
|
||||||
|
"integrity": "sha512-G2w6Nw7ZOVSzcQmsdLc0doMe64O/Sbuc2bVAbgMz6oP/6/pRStKRiVRV4bQfHp5AHYAKEGhEdVHTM+R3FDgi5w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"motion-utils": "^12.23.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/motion-utils": {
|
||||||
|
"version": "12.23.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz",
|
||||||
|
"integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
13
package.json
13
package.json
@@ -12,19 +12,20 @@
|
|||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"framer-motion": "^12.23.6",
|
||||||
|
"next": "15.4.2",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
"react-dom": "19.1.0",
|
"react-dom": "19.1.0"
|
||||||
"next": "15.4.2"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "^5",
|
"@eslint/eslintrc": "^3",
|
||||||
|
"@tailwindcss/postcss": "^4",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^19",
|
"@types/react": "^19",
|
||||||
"@types/react-dom": "^19",
|
"@types/react-dom": "^19",
|
||||||
"@tailwindcss/postcss": "^4",
|
|
||||||
"tailwindcss": "^4",
|
|
||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "15.4.2",
|
"eslint-config-next": "15.4.2",
|
||||||
"@eslint/eslintrc": "^3"
|
"tailwindcss": "^4",
|
||||||
|
"typescript": "^5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,14 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
body {
|
body {
|
||||||
@apply bg-gray-900 text-gray-200 antialiased;
|
@apply bg-gray-900 text-gray-200 antialiased;
|
||||||
}
|
}
|
||||||
|
html, body {
|
||||||
|
@apply h-full overflow-hidden;
|
||||||
|
}
|
||||||
}
|
}
|
@@ -1,9 +1,62 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useState, useEffect, useCallback } from 'react'
|
||||||
|
import Slide from '@/components/Slide';
|
||||||
|
import Slide1 from '@/components/Slide1';
|
||||||
|
import Slide2 from '@/components/Slide2';
|
||||||
|
import Slide3 from '@/components/Slide3';
|
||||||
|
import Slide4 from '@/components/Slide4';
|
||||||
|
import Slide5 from '@/components/Slide5';
|
||||||
|
|
||||||
|
|
||||||
|
const slides = [
|
||||||
|
<Slide1 key={1} />,
|
||||||
|
//<Slide2 key={2} />,
|
||||||
|
//<Slide3 key={3} />,
|
||||||
|
//<Slide4 key={4} />,
|
||||||
|
//<Slide5 key={5} />,
|
||||||
|
]
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
|
const [current, setCurrent] = useState(0)
|
||||||
|
|
||||||
|
|
||||||
|
const nextSlide = useCallback(() => {
|
||||||
|
if (current < slides.length - 1) setCurrent(current + 1)
|
||||||
|
}, [current])
|
||||||
|
|
||||||
|
const prevSlide = useCallback(() => {
|
||||||
|
if (current > 0) setCurrent(current - 1)
|
||||||
|
}, [current])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleScroll = (e: WheelEvent) => {
|
||||||
|
if (e.deltaY > 0) nextSlide()
|
||||||
|
else prevSlide()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleKey = (e: KeyboardEvent) => {
|
||||||
|
if (e.key === 'ArrowDown') nextSlide()
|
||||||
|
if (e.key === 'ArrowUp') prevSlide()
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('wheel', handleScroll)
|
||||||
|
window.addEventListener('keydown', handleKey)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('wheel', handleScroll)
|
||||||
|
window.removeEventListener('keydown', handleKey)
|
||||||
|
}
|
||||||
|
}, [current, nextSlide, prevSlide])
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen flex-col items-center justify-between p-24">
|
<main className="relative h-screen w-screen overflow-hidden">
|
||||||
<main className="flex flex-col items-center justify-center">
|
{slides.map((slide, idx) => (
|
||||||
<h1 className="text-4xl font-bold">Work in Progress</h1>
|
<Slide key={idx} index={idx} currentSlide={current}>
|
||||||
</main>
|
{slide}
|
||||||
</div>
|
</Slide>
|
||||||
);
|
))}
|
||||||
}
|
</main>
|
||||||
|
)
|
||||||
|
}
|
25
src/components/Slide.tsx
Normal file
25
src/components/Slide.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
|
type SlideProps = {
|
||||||
|
children: ReactNode
|
||||||
|
index: number
|
||||||
|
currentSlide: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Slide({ children, index, currentSlide }: SlideProps) {
|
||||||
|
const isActive = index === currentSlide
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
className={`absolute top-0 left-0 w-full h-full flex flex-col items-center justify-center text-center gap-4 text-4xl`}
|
||||||
|
initial={{ opacity: 0 }} // Start off-screen
|
||||||
|
// x to left-100% for previous slides, 0 for current, and right-100% for next slides
|
||||||
|
animate={{ x: isActive ? 0 : index < currentSlide ? '-100%' : '100%', opacity: isActive ? 1 : 0 }}
|
||||||
|
transition={{ duration: 0.8 }}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</motion.div>
|
||||||
|
)
|
||||||
|
}
|
106
src/components/Slide1.tsx
Normal file
106
src/components/Slide1.tsx
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { motion, MotionValue, useTime, useTransform } from 'framer-motion';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
const titles = [
|
||||||
|
'Full Stack Developer',
|
||||||
|
'DevOps Engineer',
|
||||||
|
'Game Developer',
|
||||||
|
'Open Source Contributor',
|
||||||
|
'Tech Enthusiast',
|
||||||
|
'Gamer',
|
||||||
|
];
|
||||||
|
|
||||||
|
function Title({ title, progress, range, ys, os }: { title: string, progress: MotionValue<number>, range: number[], ys: string[], os: number[] }) {
|
||||||
|
const y = useTransform(progress, range, ys);
|
||||||
|
const opacity = useTransform(progress, range, os);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
className="absolute w-full text-center text-4xl font-bold"
|
||||||
|
style={{
|
||||||
|
y,
|
||||||
|
opacity
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Slide1() {
|
||||||
|
const time = useTime();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const genRanges = useMemo(() => {
|
||||||
|
const ranges = [];
|
||||||
|
for (let i = 0; i < titles.length+1; i++) {
|
||||||
|
const start = i * 1500 + 250 * i;
|
||||||
|
const end = start + 1500;
|
||||||
|
ranges.push(start, end);
|
||||||
|
}
|
||||||
|
return ranges;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const genYs = useMemo(() => {
|
||||||
|
const ys = [];
|
||||||
|
for (let i = 0; i < titles.length; i++) {
|
||||||
|
ys.push([]);
|
||||||
|
for (let j = 0; j < titles.length+1; j++) {
|
||||||
|
if ((j % titles.length) === i) {
|
||||||
|
ys[i].push('0%', '0%'); // Current title
|
||||||
|
} else if (j < i) {
|
||||||
|
ys[i].push('100%', '100%'); // Previous titles
|
||||||
|
} else if (j == i + 1) {
|
||||||
|
ys[i].push('-100%', '100%'); // Next title
|
||||||
|
} else {
|
||||||
|
ys[i].push('100%', '100%'); // Titles after next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ys;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const genOs = useMemo(() => {
|
||||||
|
const os = [];
|
||||||
|
for (let i = 0; i < titles.length; i++) {
|
||||||
|
os.push([]);
|
||||||
|
for (let j = 0; j < titles.length+1; j++) {
|
||||||
|
if ((j % titles.length) === i) {
|
||||||
|
os[i].push(1, 1); // Current title
|
||||||
|
} else if (j < i) {
|
||||||
|
os[i].push(0, 0); // Previous titles
|
||||||
|
} else if (j == i + 1) {
|
||||||
|
os[i].push(0, 0); // Next title
|
||||||
|
} else {
|
||||||
|
os[i].push(0, 0); // Titles after next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const progress = useTransform(time, (t) => ((t * 2) % genRanges[genRanges.length - 2]));
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p>I'm Kay</p>
|
||||||
|
<div className="relative flex flex-col h-10 w-full">
|
||||||
|
{titles.map((title, i) => (
|
||||||
|
<Title
|
||||||
|
key={i}
|
||||||
|
title={title}
|
||||||
|
progress={progress}
|
||||||
|
range={genRanges}
|
||||||
|
ys={genYs[i]}
|
||||||
|
os={genOs[i]}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
8
src/components/Slide2.tsx
Normal file
8
src/components/Slide2.tsx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
export default function Slide2() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
8
src/components/Slide3.tsx
Normal file
8
src/components/Slide3.tsx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
export default function Slide3() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
8
src/components/Slide4.tsx
Normal file
8
src/components/Slide4.tsx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
export default function Slide4() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
8
src/components/Slide5.tsx
Normal file
8
src/components/Slide5.tsx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
export default function Slide5() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
Reference in New Issue
Block a user