Picture Replaced
This commit is contained in:
parent
758518f7ef
commit
607dd617f6
3 changed files with 57 additions and 84 deletions
50
src/App.tsx
50
src/App.tsx
|
|
@ -1,63 +1,43 @@
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from "react";
|
||||||
import Top from "@/components/Home/Top";
|
import Top from "@/components/Home/Top";
|
||||||
import About from "@/components/Home/About";
|
import About from "@/components/Home/About";
|
||||||
import Timeline from "@/components/Home/Timeline";
|
import Timeline from "@/components/Home/Timeline";
|
||||||
import Contact from '@/components/Home/Contact';
|
import Contact from "@/components/Home/Contact";
|
||||||
import Project from '@/components/Home/Project';
|
import Project from "@/components/Home/Project";
|
||||||
|
|
||||||
import './index.css';
|
import "./index.css";
|
||||||
|
|
||||||
export function App() {
|
export function App() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 초기 로드 시 hash에 맞게 스크롤
|
// img 위에서 스크롤 방지
|
||||||
const scrollToHash = () => {
|
const handleWheel = (event) => {
|
||||||
const hash = window.location.hash.substring(1);
|
if (event.target.tagName.toLowerCase() === "img") {
|
||||||
if (hash) {
|
event.preventDefault();
|
||||||
const element = document.getElementById(hash);
|
|
||||||
if (element) {
|
|
||||||
setTimeout(() => {
|
|
||||||
element.scrollIntoView({ behavior: "smooth" });
|
|
||||||
}, 100); // 브라우저가 레이아웃을 그릴 시간을 줌
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
scrollToHash();
|
|
||||||
|
|
||||||
// 스크롤 시 hash 업데이트 로직
|
document.addEventListener("wheel", handleWheel, { passive: false });
|
||||||
const sections = document.querySelectorAll(".section");
|
|
||||||
const observer = new IntersectionObserver(
|
|
||||||
(entries) => {
|
|
||||||
entries.forEach(entry => {
|
|
||||||
if (entry.isIntersecting) {
|
|
||||||
window.history.replaceState(null, "", `#${entry.target.id}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
{ threshold: 0.6 } // 60% 보이면 활성화
|
|
||||||
);
|
|
||||||
|
|
||||||
sections.forEach(section => observer.observe(section));
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
sections.forEach(section => observer.unobserve(section));
|
document.removeEventListener("wheel", handleWheel);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="fullpage" className="bg-background text-foreground w-full">
|
<div id="fullpage" className="bg-background text-foreground w-full">
|
||||||
<div id="top" className="bg-background text-foreground w-full h-screen flex items-center justify-center section">
|
<div id="top" className="section">
|
||||||
<Top />
|
<Top />
|
||||||
</div>
|
</div>
|
||||||
<div id="about" className="bg-background text-foreground w-full h-screen flex items-center justify-center section">
|
<div id="about" className="section">
|
||||||
<About />
|
<About />
|
||||||
</div>
|
</div>
|
||||||
<div id="project" className="bg-background text-foreground w-full h-screen flex items-center justify-center section">
|
<div id="project" className="section">
|
||||||
<Project />
|
<Project />
|
||||||
</div>
|
</div>
|
||||||
<div id="timeline" className="bg-background text-foreground w-full h-screen flex items-center justify-center section">
|
<div id="timeline" className="section">
|
||||||
<Timeline />
|
<Timeline />
|
||||||
</div>
|
</div>
|
||||||
<div id="contact" className="bg-background text-foreground w-full h-screen flex items-center justify-center section">
|
<div id="contact" className="section">
|
||||||
<Contact />
|
<Contact />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,30 @@
|
||||||
import {
|
import { useState } from "react";
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipProvider,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "@/components/ui/tooltip";
|
|
||||||
import "../../index.css";
|
import "../../index.css";
|
||||||
import Image from "../../profile.avif";
|
|
||||||
|
|
||||||
export default function Top() {
|
export default function Top() {
|
||||||
|
const [mousePos, setMousePos] = useState({ x: 50, y: 50 });
|
||||||
|
const [scale, setScale] = useState(1.15);
|
||||||
|
const [isHovering, setIsHovering] = useState(false);
|
||||||
|
|
||||||
|
const handleMouseMove = (e) => {
|
||||||
|
const { left, top, width, height } = e.currentTarget.getBoundingClientRect();
|
||||||
|
const x = ((e.clientX - left) / width) * 100;
|
||||||
|
const y = ((e.clientY - top) / height) * 100;
|
||||||
|
|
||||||
|
setMousePos((prev) => ({
|
||||||
|
x: prev.x + (x - prev.x) * 0.1,
|
||||||
|
y: prev.y + (y - prev.y) * 0.1,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleWheel = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setScale((prev) => {
|
||||||
|
const newScale = prev + e.deltaY * -0.0025;
|
||||||
|
return Math.min(Math.max(newScale, 1), 3);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-background text-foreground w-full h-screen flex items-center justify-center">
|
<div className="bg-background text-foreground w-full h-screen flex items-center justify-center">
|
||||||
<div className="flex flex-col md:flex-row w-full md:w-[50%] h-full py-16 md:py-32">
|
<div className="flex flex-col md:flex-row w-full md:w-[50%] h-full py-16 md:py-32">
|
||||||
|
|
@ -24,29 +41,24 @@ export default function Top() {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<TooltipProvider delayDuration={0}>
|
<div
|
||||||
<Tooltip>
|
className="md:w-[55%] select-none p-5 md:p-0 w-full h-full flex justify-center md:justify-end items-end rounded-3xl mb-8 md:mb-0 overflow-hidden"
|
||||||
<TooltipTrigger asChild>
|
onMouseMove={handleMouseMove}
|
||||||
<div className="md:w-[55%] select-none avatar-background w-full h-full flex justify-center md:justify-end items-end rounded-3xl mb-8 md:mb-0">
|
onMouseEnter={() => setIsHovering(true)}
|
||||||
<div className="w-full h-full flex justify-center md:justify-end items-end avatar-background-blur">
|
onMouseLeave={() => setIsHovering(false)}
|
||||||
|
onWheel={handleWheel}
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
src={Image}
|
src="https://f.imnya.ng/profile/b.avif"
|
||||||
className="w-[60%] max-w-[360px] select-none"
|
alt="Me"
|
||||||
title="Special Thanks to @dob2_"
|
className="w-full h-full object-cover rounded-3xl transition-transform duration-500 ease-out"
|
||||||
|
style={{
|
||||||
|
transform: `scale(${isHovering ? scale : 1})`,
|
||||||
|
transformOrigin: `${mousePos.x}% ${mousePos.y}%`,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent
|
|
||||||
align="end"
|
|
||||||
side="bottom"
|
|
||||||
className="px-2 py-1 text-xs"
|
|
||||||
>
|
|
||||||
Special Thanks to @dob2_
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</TooltipProvider>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,22 +13,3 @@
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar-background {
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
.avatar-background {
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
background-image: url("https://f.imnya.ng/profile/banner.CjixG8N2_15mivN.webp");
|
|
||||||
background-size: cover;
|
|
||||||
background-position: 400px center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-attachment: fixed;
|
|
||||||
}
|
|
||||||
.avatar-background-blur {
|
|
||||||
backdrop-filter: blur(5px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue