This commit is contained in:
46
package-lock.json
generated
46
package-lock.json
generated
@@ -8,6 +8,7 @@
|
||||
"name": "homepage",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"framer-motion": "^12.23.6",
|
||||
"next": "15.4.2",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0"
|
||||
@@ -22,6 +23,9 @@
|
||||
"eslint-config-next": "15.4.2",
|
||||
"tailwindcss": "^4",
|
||||
"typescript": "^5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@alloc/quick-lru": {
|
||||
@@ -3326,6 +3330,33 @@
|
||||
"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": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
@@ -4598,6 +4629,21 @@
|
||||
"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": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
|
13
package.json
13
package.json
@@ -12,19 +12,20 @@
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"framer-motion": "^12.23.6",
|
||||
"next": "15.4.2",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"next": "15.4.2"
|
||||
"react-dom": "19.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5",
|
||||
"@eslint/eslintrc": "^3",
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"tailwindcss": "^4",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "15.4.2",
|
||||
"@eslint/eslintrc": "^3"
|
||||
"tailwindcss": "^4",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,14 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
body {
|
||||
@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() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col items-center justify-between p-24">
|
||||
<main className="flex flex-col items-center justify-center">
|
||||
<h1 className="text-4xl font-bold">Work in Progress</h1>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
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 (
|
||||
<main className="relative h-screen w-screen overflow-hidden">
|
||||
{slides.map((slide, idx) => (
|
||||
<Slide key={idx} index={idx} currentSlide={current}>
|
||||
{slide}
|
||||
</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