FullPage Scroll은 나쁩니다. && 이거 솔직히 추가해줘야한다고 봄
This commit is contained in:
parent
3a6fffedb9
commit
296439b044
13 changed files with 69 additions and 72 deletions
4
.github/workflows/main.yml
vendored
4
.github/workflows/main.yml
vendored
|
|
@ -17,9 +17,11 @@ jobs:
|
||||||
needs: checkout
|
needs: checkout
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- run: pwd && ls -al
|
- run: cd /home/neko/Workspace/Git/imnyang/imnyang
|
||||||
|
- run: git pull
|
||||||
- run: bun install
|
- run: bun install
|
||||||
- run: bun run build
|
- run: bun run build
|
||||||
|
- run: pm2 restart imnya.ng
|
||||||
|
|
||||||
replace:
|
replace:
|
||||||
name: Replace Old Build with New Build
|
name: Replace Old Build with New Build
|
||||||
|
|
|
||||||
3
bun.lock
3
bun.lock
|
|
@ -18,6 +18,7 @@
|
||||||
"react": "^19",
|
"react": "^19",
|
||||||
"react-dom": "^19",
|
"react-dom": "^19",
|
||||||
"react-hook-form": "^7.54.2",
|
"react-hook-form": "^7.54.2",
|
||||||
|
"serve-static-bun": "^0.5.3",
|
||||||
"tailwind-merge": "^3.0.1",
|
"tailwind-merge": "^3.0.1",
|
||||||
"tailwindcss": "^4.0.6",
|
"tailwindcss": "^4.0.6",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
|
|
@ -151,6 +152,8 @@
|
||||||
|
|
||||||
"scheduler": ["scheduler@0.25.0", "", {}, "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA=="],
|
"scheduler": ["scheduler@0.25.0", "", {}, "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA=="],
|
||||||
|
|
||||||
|
"serve-static-bun": ["serve-static-bun@0.5.3", "", {}, "sha512-QlfA/Z30MwZl4XXWM9KevfinJRJjzJMRK8sXABbaY06Y7KTuXtbT1n0e8qdf1PgM59mpgSh/JTUM9Jjsh0E58Q=="],
|
||||||
|
|
||||||
"tailwind-merge": ["tailwind-merge@3.0.1", "", {}, "sha512-AvzE8FmSoXC7nC+oU5GlQJbip2UO7tmOhOfQyOmPhrStOGXHU08j8mZEHZ4BmCqY5dWTCo4ClWkNyRNx1wpT0g=="],
|
"tailwind-merge": ["tailwind-merge@3.0.1", "", {}, "sha512-AvzE8FmSoXC7nC+oU5GlQJbip2UO7tmOhOfQyOmPhrStOGXHU08j8mZEHZ4BmCqY5dWTCo4ClWkNyRNx1wpT0g=="],
|
||||||
|
|
||||||
"tailwindcss": ["tailwindcss@4.0.8", "", {}, "sha512-Me7N5CKR+D2A1xdWA5t5+kjjT7bwnxZOE6/yDI/ixJdJokszsn2n++mdU5yJwrsTpqFX2B9ZNMBJDwcqk9C9lw=="],
|
"tailwindcss": ["tailwindcss@4.0.8", "", {}, "sha512-Me7N5CKR+D2A1xdWA5t5+kjjT7bwnxZOE6/yDI/ixJdJokszsn2n++mdU5yJwrsTpqFX2B9ZNMBJDwcqk9C9lw=="],
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
"react": "^19",
|
"react": "^19",
|
||||||
"react-dom": "^19",
|
"react-dom": "^19",
|
||||||
"react-hook-form": "^7.54.2",
|
"react-hook-form": "^7.54.2",
|
||||||
|
"serve-static-bun": "^0.5.3",
|
||||||
"tailwind-merge": "^3.0.1",
|
"tailwind-merge": "^3.0.1",
|
||||||
"tailwindcss": "^4.0.6",
|
"tailwindcss": "^4.0.6",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
|
|
|
||||||
23
src/App.tsx
23
src/App.tsx
|
|
@ -25,7 +25,7 @@ export function App() {
|
||||||
scrollToHash();
|
scrollToHash();
|
||||||
|
|
||||||
// 스크롤 시 hash 업데이트 로직
|
// 스크롤 시 hash 업데이트 로직
|
||||||
const sections = document.querySelectorAll(".section");
|
const sections = document.querySelectorAll(".hash");
|
||||||
const observer = new IntersectionObserver(
|
const observer = new IntersectionObserver(
|
||||||
(entries) => {
|
(entries) => {
|
||||||
entries.forEach(entry => {
|
entries.forEach(entry => {
|
||||||
|
|
@ -34,7 +34,7 @@ export function App() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{ threshold: 0.6 } // 60% 보이면 활성화
|
{ threshold: 0.9 } // 90% 보이면 활성화
|
||||||
);
|
);
|
||||||
|
|
||||||
sections.forEach(section => observer.observe(section));
|
sections.forEach(section => observer.observe(section));
|
||||||
|
|
@ -44,10 +44,11 @@ export function App() {
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// img 위에서 스크롤 방지
|
// img 위에서 스크롤 방지
|
||||||
const handleWheel = (event) => {
|
const handleWheel = (event: WheelEvent) => {
|
||||||
if (event.target.tagName.toLowerCase() === "img") {
|
if (event.target instanceof HTMLImageElement) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -60,23 +61,23 @@ export function App() {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="fullpage" className="bg-background text-foreground w-full h-screen max-h-screen">
|
<div className="bg-background text-foreground w-full h-full overflow-y-auto">
|
||||||
<div id="top" className="section">
|
<div id="top" className="hash"> {/* stupid copliot this is not section */}
|
||||||
<Top />
|
<Top />
|
||||||
</div>
|
</div>
|
||||||
<div id="about" className="section">
|
<div id="about" className="section hash"> {/* Hey Stupid Copliot This is section */}
|
||||||
<About />
|
<About />
|
||||||
</div>
|
</div>
|
||||||
<div id="wakatime" className="section">
|
<div id="wakatime" className="section hash"> {/* Hey Stupid Copliot This is section */}
|
||||||
<Wakatime />
|
<Wakatime />
|
||||||
</div>
|
</div>
|
||||||
<div id="project" className="section">
|
<div id="project" className="section hash"> {/* Hey Stupid Copliot This is section */}
|
||||||
<Project />
|
<Project />
|
||||||
</div>
|
</div>
|
||||||
<div id="timeline" className="section">
|
<div id="timeline" className="section hash"> {/* Hey Stupid Copliot This is section */}
|
||||||
<Timeline />
|
<Timeline />
|
||||||
</div>
|
</div>
|
||||||
<div id="contact" className="section">
|
<div id="contact" className="section hash"> {/* Hey Stupid Copliot This is section */}
|
||||||
<Contact />
|
<Contact />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -63,15 +63,29 @@ export default function About() {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// isVisible이 true일 때 time 증가
|
if (!isVisible) return;
|
||||||
if (isVisible && time < totalSeconds) {
|
|
||||||
const timer = setTimeout(() => setTime(prevTime => prevTime + 1), time === 0 ? 300 : 0);
|
const start = Date.now() - time;
|
||||||
return () => clearTimeout(timer);
|
|
||||||
|
let animationFrameId: number;
|
||||||
|
|
||||||
|
const tick = () => {
|
||||||
|
const elapsed = Date.now() - start;
|
||||||
|
if (elapsed >= totalSeconds) {
|
||||||
|
setTime(totalSeconds);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}, [isVisible, time, totalSeconds]);
|
setTime(elapsed);
|
||||||
|
animationFrameId = requestAnimationFrame(tick);
|
||||||
|
};
|
||||||
|
|
||||||
|
animationFrameId = requestAnimationFrame(tick);
|
||||||
|
|
||||||
|
return () => cancelAnimationFrame(animationFrameId);
|
||||||
|
}, [isVisible, totalSeconds]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-screen flex flex-col items-center justify-center" ref={AboutRef}>
|
<div className="w-full flex flex-col items-center justify-center" ref={AboutRef}>
|
||||||
<div className="w-full md:w-[50%] p-4">
|
<div className="w-full md:w-[50%] p-4">
|
||||||
<h1 className="text-2xl font-bold">🤔 About</h1>
|
<h1 className="text-2xl font-bold">🤔 About</h1>
|
||||||
<div className="flex items-start justify-center flex-col p-2 mt-2 w-full">
|
<div className="flex items-start justify-center flex-col p-2 mt-2 w-full">
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import { Button } from "../ui/button";
|
||||||
|
|
||||||
export default function Contact() {
|
export default function Contact() {
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-screen flex items-center justify-center">
|
<div className="w-full h-64 flex items-center justify-center">
|
||||||
<div className="w-full md:w-[50%] p-4 flex items-center justify-center flex-col gap-4">
|
<div className="w-full md:w-[50%] p-4 flex items-center justify-center flex-col gap-4">
|
||||||
<div className="flex items-center justify-center gap-4 flex-row">
|
<div className="flex items-center justify-center gap-4 flex-row">
|
||||||
<TooltipProvider delayDuration={0}>
|
<TooltipProvider delayDuration={0}>
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ const projects = [
|
||||||
|
|
||||||
export default function Project() {
|
export default function Project() {
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-screen flex flex-col items-center justify-center select-none">
|
<div className="w-full flex flex-col items-center justify-center select-none">
|
||||||
<div className="w-full md:w-[50%] p-4">
|
<div className="w-full md:w-[50%] p-4">
|
||||||
<h1 className="text-2xl font-bold">📖 Project</h1>
|
<h1 className="text-2xl font-bold">📖 Project</h1>
|
||||||
<div className="mt-4 gap-4 grid grid-cols-1 md:grid-cols-2">
|
<div className="mt-4 gap-4 grid grid-cols-1 md:grid-cols-2">
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,7 @@ export default function Timeline() {
|
||||||
<h1 className="text-2xl font-bold mb-4 w-full">🌠 Timeline</h1>
|
<h1 className="text-2xl font-bold mb-4 w-full">🌠 Timeline</h1>
|
||||||
<p>현재까지 {count}개의 개성있는 조각들이 모였어요!</p>
|
<p>현재까지 {count}개의 개성있는 조각들이 모였어요!</p>
|
||||||
<br/>
|
<br/>
|
||||||
<Accordion type="single" collapsible className="w-full space-y-2">
|
<Accordion type="multiple" className="w-full space-y-2">
|
||||||
{Array.from(new Set(events.map(event => new Date(event.date).getFullYear()))).sort((a, b) => b - a).map(year => (
|
{Array.from(new Set(events.map(event => new Date(event.date).getFullYear()))).sort((a, b) => b - a).map(year => (
|
||||||
<AccordionItem
|
<AccordionItem
|
||||||
value={year.toString()}
|
value={year.toString()}
|
||||||
|
|
@ -199,7 +199,7 @@ export default function Timeline() {
|
||||||
/>
|
/>
|
||||||
</AccordionPrimitive.Trigger>
|
</AccordionPrimitive.Trigger>
|
||||||
</AccordionPrimitive.Header>
|
</AccordionPrimitive.Header>
|
||||||
<AccordionContent className="pb-2 ps-7 text-foreground max-h-60 overflow-y-auto">
|
<AccordionContent className="pb-2 ps-7 text-foreground overflow-y-auto">
|
||||||
{events.filter(event => new Date(event.date).getFullYear() === year).map((event, index) => (
|
{events.filter(event => new Date(event.date).getFullYear() === year).map((event, index) => (
|
||||||
<div key={index} className="my-2">
|
<div key={index} className="my-2">
|
||||||
<p className="flex flex-row"><p className="text-md font-semibold fixed-width-number">{new Date(event.date).toLocaleDateString('en-US', { month: 'short', day: '2-digit' })}</p><p className="text-md font-semibold fixed-width-number text-muted-foreground">ㆍ{event.category}</p></p>
|
<p className="flex flex-row"><p className="text-md font-semibold fixed-width-number">{new Date(event.date).toLocaleDateString('en-US', { month: 'short', day: '2-digit' })}</p><p className="text-md font-semibold fixed-width-number text-muted-foreground">ㆍ{event.category}</p></p>
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ export default function Wakatime() {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-screen flex flex-col items-center justify-center">
|
<div className="w-full flex flex-col items-center justify-center">
|
||||||
<div className="w-full md:w-[50%] p-4">
|
<div className="w-full md:w-[50%] p-4">
|
||||||
<a className="text-2xl font-bold" href="https://wakatime.com/@imnyang" target="_blank" rel="noopener noreferrer">🍝 Wakatime</a>
|
<a className="text-2xl font-bold" href="https://wakatime.com/@imnyang" target="_blank" rel="noopener noreferrer">🍝 Wakatime</a>
|
||||||
<p>Dashboards for developers</p>
|
<p>Dashboards for developers</p>
|
||||||
|
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
<svg width="64" height="43" viewBox="0 0 64 43" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<mask id="mask0_296_26" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="64" height="43">
|
|
||||||
<path d="M64 0H0V42.6667H64V0Z" fill="white"/>
|
|
||||||
</mask>
|
|
||||||
<g mask="url(#mask0_296_26)">
|
|
||||||
<path d="M0 5.00001C0 2.23858 2.23858 0 5 0H59C61.7614 0 64 2.23858 64 5V37.6667C64 40.4281 61.7614 42.6667 59 42.6667H5C2.23858 42.6667 0 40.4281 0 37.6667V5.00001Z" fill="white"/>
|
|
||||||
<path d="M23.125 15.4168C24.6943 13.063 27.1344 11.4289 29.9083 10.8742C32.6824 10.3194 35.5632 10.8893 37.9171 12.4586C40.2709 14.0278 41.9051 16.4678 42.4597 19.242C43.0144 22.0158 42.4445 24.8966 40.8752 27.2505L23.125 15.4168Z" fill="#D0303C"/>
|
|
||||||
<path d="M23.1246 15.417C21.5553 17.7708 20.9854 20.6516 21.5402 23.4257C22.0949 26.1996 23.729 28.6396 26.0828 30.2089C28.4367 31.7783 31.3175 32.3481 34.0916 31.7935C36.8655 31.2385 39.3055 29.6047 40.8748 27.2508C41.6593 26.0737 41.9444 24.6335 41.6671 23.2465C41.3897 21.8593 40.5727 20.6393 39.3956 19.8548C38.2188 19.0703 36.7783 18.7852 35.3913 19.0625C34.0044 19.3399 32.7844 20.1569 31.9996 21.334L23.1246 15.417Z" fill="#134A9D"/>
|
|
||||||
<path d="M32.001 21.3344C33.6349 18.8835 32.9727 15.5722 30.5218 13.9383C28.0711 12.3044 24.7597 12.9666 23.1258 15.4174C21.4919 17.8683 22.1541 21.1795 24.6049 22.8133C27.0556 24.4472 30.3671 23.7851 32.001 21.3344Z" fill="#D0303C"/>
|
|
||||||
<path d="M9.8125 12.9504L15.7293 4.0752L17.2085 5.06133L11.2917 13.9365L9.8125 12.9504ZM12.0313 14.4296L17.9481 5.5544L19.4273 6.54053L13.5105 15.4157L12.0313 14.4296ZM14.2501 15.9088L20.1669 7.0336L21.6461 8.01973L15.7293 16.8949L14.2501 15.9088Z" fill="black"/>
|
|
||||||
<path d="M51.4763 33.7834L54.188 29.7156L52.7088 28.7295L49.9971 32.7972L51.4763 33.7834Z" fill="black"/>
|
|
||||||
<path d="M49.2575 32.3039L51.9693 28.2361L50.4901 27.25L47.7783 31.3177L49.2575 32.3039Z" fill="black"/>
|
|
||||||
<path d="M47.2852 32.0576L48.7644 33.0438L46.0524 37.1117L44.5732 36.1256L47.2852 32.0576Z" fill="black"/>
|
|
||||||
<path d="M47.0388 30.8244L49.7505 26.7566L48.2713 25.7705L45.5596 29.8382L47.0388 30.8244Z" fill="black"/>
|
|
||||||
<path d="M45.0665 30.5781L46.5457 31.5643L43.8337 35.6323L42.3545 34.6461L45.0665 30.5781Z" fill="black"/>
|
|
||||||
<path d="M49.504 33.5361L50.9832 34.5223L48.2712 38.5903L46.792 37.6041L49.504 33.5361Z" fill="black"/>
|
|
||||||
<path d="M46.5457 11.1021L43.8337 7.03418L42.3545 8.02031L45.0665 12.0883L46.5457 11.1021Z" fill="black"/>
|
|
||||||
<path d="M45.5596 12.827L48.2713 16.8946L49.7505 15.9085L47.0388 11.8408L45.5596 12.827Z" fill="black"/>
|
|
||||||
<path d="M50.49 15.416L44.5732 6.54082L46.0524 5.55469L51.9692 14.4299L50.49 15.416Z" fill="black"/>
|
|
||||||
<path d="M52.7088 13.9367L49.9971 9.86895L51.4763 8.88281L54.188 12.9505L52.7088 13.9367Z" fill="black"/>
|
|
||||||
<path d="M50.9832 8.14306L49.504 9.12919L46.792 5.06133L48.2712 4.0752L50.9832 8.14306Z" fill="black"/>
|
|
||||||
<path d="M9.8125 29.7156L15.7293 38.5908L17.2085 37.6047L11.2917 28.7295L9.8125 29.7156Z" fill="black"/>
|
|
||||||
<path d="M15.2373 33.0437L17.9491 37.1115L19.4283 36.1254L16.7165 32.0576L15.2373 33.0437Z" fill="black"/>
|
|
||||||
<path d="M14.7431 32.3039L16.2223 31.3177L13.5104 27.25L12.0312 28.2361L14.7431 32.3039Z" fill="black"/>
|
|
||||||
<path d="M14.25 26.7566L20.1668 35.6318L21.646 34.6457L15.7292 25.7705L14.25 26.7566Z" fill="black"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 3.2 KiB |
|
|
@ -1,21 +1,13 @@
|
||||||
@import "../styles/globals.css";
|
@import "../styles/globals.css";
|
||||||
|
|
||||||
#fullpage {
|
|
||||||
scroll-snap-type: y mandatory;
|
|
||||||
overflow-y: scroll;
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
.section {
|
.section {
|
||||||
scroll-snap-align: start;
|
@apply py-16 border-b-1 border-muted;
|
||||||
min-height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
body {
|
html,
|
||||||
@apply min-h-screen m-0 p-0 overflow-hidden;
|
body,
|
||||||
|
#root {
|
||||||
|
@apply h-full m-0 p-0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
<title>남현석 | :two_hearts:</title>
|
<title>남현석 | :two_hearts:</title>
|
||||||
<meta name="description" content="항상 탐구하고 연구하는 평범한 학생 개발자입니다." />
|
<meta name="description" content="항상 탐구하고 연구하는 평범한 학생 개발자입니다." />
|
||||||
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-4588517451789913" crossorigin="anonymous"></script>
|
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-4588517451789913" crossorigin="anonymous"></script>
|
||||||
<script type="module" src="./frontend.tsx" async></script>'
|
<script type="module" src="./frontend.tsx" async></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,27 @@
|
||||||
import { serve } from "bun";
|
import { serve } from "bun";
|
||||||
import index from "./index.html";
|
import index from "./index.html";
|
||||||
|
import serveStatic from "serve-static-bun";
|
||||||
|
|
||||||
|
// Parse command line arguments for port
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
const portArgIndex = args.findIndex(arg => arg === "--port");
|
||||||
|
const port = portArgIndex !== -1 && args[portArgIndex + 1] ?
|
||||||
|
parseInt(args[portArgIndex + 1]) : 3000;
|
||||||
|
|
||||||
const server = serve({
|
const server = serve({
|
||||||
|
port: port,
|
||||||
routes: {
|
routes: {
|
||||||
// Serve index.html for all unmatched routes.
|
// Serve index.html for all unmatched routes.
|
||||||
"/*": index,
|
"/*": index,
|
||||||
'/timeline': Response.redirect("/#timeline"),
|
"/timeline": Response.redirect("/#timeline"),
|
||||||
'/ads.txt': new Response("google.com, pub-4588517451789913, DIRECT, f08c47fec0942fa0", {
|
"/ads.txt": new Response(
|
||||||
|
"google.com, pub-4588517451789913, DIRECT, f08c47fec0942fa0",
|
||||||
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": "text/plain",
|
"content-type": "text/plain",
|
||||||
},
|
},
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
},
|
},
|
||||||
development: process.env.NODE_ENV !== "production",
|
development: process.env.NODE_ENV !== "production",
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue