Skip to content

Commit 54c9fb9

Browse files
authored
Merge pull request #62 from LIKELION-SOGANG/feature/imporved-header#61
헤더 사용선 개선 및 페이지 이동 시 전환 효과 추가
2 parents 13da210 + de19293 commit 54c9fb9

File tree

10 files changed

+200
-64
lines changed

10 files changed

+200
-64
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* @rmdnps10 @shu07002 @chlqhgus @Kimbyul88 @minwoooya @seoooa

package-lock.json

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"eslint-config-prettier": "^9.1.0",
4242
"eslint-plugin-prettier": "^5.2.1",
4343
"eslint-plugin-unused-imports": "^4.1.4",
44+
"framer-motion": "^11.11.8",
4445
"husky": "^8.0.0",
4546
"postcss": "^8",
4647
"prettier": "^3.3.3",

src/app/component/Header.tsx

Lines changed: 57 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,51 @@
11
'use client'
2-
import { HEADER, MOBILE_MENU_LIST } from '@/style/zIndex'
3-
import Image from 'next/image'
42
import Link from 'next/link'
5-
import React, { useState, useEffect } from 'react'
6-
import { useLockBodyScroll } from 'react-use'
7-
3+
import Image from 'next/image'
4+
import { HEADER, MOBILE_MENU_LIST } from '@/style/zIndex'
5+
import useScrollDirection from '@/hooks/useScrollDirection'
6+
import useMobileMenu from '@/hooks/useMobileMenu'
7+
import { usePathname } from 'next/navigation'
8+
import { AnimatePresence } from 'framer-motion'
9+
import Loading from './Loading'
10+
import useLoadingProgress from '@/hooks/useLoadingProgress'
811
//
912
//
1013
//
11-
1214
export default function Header() {
13-
const [isShowHeader, setIsShowHeader] = useState(true)
14-
const [lastScrollY, setLastScrollY] = useState(0)
15-
const [isShowMobileMenu, setIsShowMobileMenu] = useState(false)
16-
useLockBodyScroll(isShowMobileMenu)
15+
const pathname = usePathname()
16+
const isShowHeader = useScrollDirection()
17+
const { isShowMobileMenu, showMobileMenu, hideMobileMenu } = useMobileMenu()
18+
const { isLoading, loadingProgress, startLoading, setLoadingSpeed } =
19+
useLoadingProgress(pathname)
1720

18-
const handleMobileHamburgerClick = () => {
19-
setIsShowMobileMenu(true)
21+
const handleMobileLinkClick = () => {
22+
hideMobileMenu()
23+
startLoading()
24+
setLoadingSpeed(5)
2025
}
2126

22-
const handleMobileCloseMenuClick = () => {
23-
setIsShowMobileMenu(false)
27+
const handleLinkClick = () => {
28+
startLoading()
29+
setLoadingSpeed(5)
2430
}
2531

26-
useEffect(() => {
27-
const handleScroll = () => {
28-
if (typeof window !== 'undefined') {
29-
const currentScrollY = window.scrollY
30-
if (currentScrollY > lastScrollY) {
31-
setIsShowHeader(false)
32-
} else {
33-
setIsShowHeader(true)
34-
}
35-
setLastScrollY(currentScrollY)
36-
}
37-
}
32+
const getLinkClass = (path: string) => {
33+
return pathname === path
34+
? 'font-pp italic text-HeaderPC px-[0.3rem] relative after:content-[""] after:absolute after:left-0 after:bottom-0 after:h-[2px] after:bg-white after:w-full'
35+
: 'font-pp font-light italic text-HeaderPC px-[0.3rem] relative after:content-[""] after:absolute after:left-1/2 after:bottom-0 after:h-[2px] after:bg-white after:w-0 hover:after:w-full hover:after:left-0 after:transition-all after:duration-300'
36+
}
3837

39-
if (typeof window !== 'undefined') {
40-
window.addEventListener('scroll', handleScroll)
41-
return () => {
42-
window.removeEventListener('scroll', handleScroll)
43-
}
44-
}
45-
}, [lastScrollY])
38+
const getMobileLinkClass = (path: string) => {
39+
return pathname === path
40+
? 'px-[3rem] py-[1.1rem] block border-b border-white underline'
41+
: 'px-[3rem] py-[1.1rem] block border-b border-white'
42+
}
4643

4744
return (
4845
<>
46+
<AnimatePresence>
47+
{isLoading && <Loading loadingProgress={loadingProgress} />}
48+
</AnimatePresence>
4949
<header
5050
className={`text-white mix-blend-difference w-full flex fixed top-0 p-[2.5rem] transition-transform duration-500 ${
5151
isShowHeader ? 'translate-y-0' : '-translate-y-full'
@@ -60,35 +60,40 @@ export default function Header() {
6060
<nav className="ml-auto hidden tablet:flex gap-[1.5rem]">
6161
<Link
6262
href={'/about'}
63-
className="font-pp italic text-HeaderPC p-[0.5rem]">
63+
onClick={handleLinkClick}
64+
className={getLinkClass('/about')}>
6465
About
6566
</Link>
6667
<Link
6768
href={'/project'}
68-
className="font-pp italic text-HeaderPC p-[0.5rem]">
69+
onClick={handleLinkClick}
70+
className={getLinkClass('/project')}>
6971
Project
7072
</Link>
7173
<Link
7274
href={'/people'}
73-
className="font-pp italic text-HeaderPC p-[0.5rem]">
75+
onClick={handleLinkClick}
76+
className={getLinkClass('/people')}>
7477
People
7578
</Link>
7679
<Link
7780
href={'/recruit'}
78-
className="font-pp italic text-HeaderPC p-[0.5rem]">
81+
onClick={handleLinkClick}
82+
className={getLinkClass('/recruit')}>
7983
Recruit
8084
</Link>
8185
<Link
8286
href={'/contact'}
83-
className="font-pp italic text-HeaderPC p-[0.5rem]">
87+
onClick={handleLinkClick}
88+
className={getLinkClass('/contact')}>
8489
Contact
8590
</Link>
8691
</nav>
8792
{/* 모바일 */}
8893
{/* 햄버거 버튼 */}
8994
<button
9095
className="block ml-auto tablet:hidden py-[1rem] pl-[1rem] "
91-
onClick={handleMobileHamburgerClick}>
96+
onClick={showMobileMenu}>
9297
<Image
9398
src={'/icon/button/hamburger-white.svg'}
9499
width={20}
@@ -104,7 +109,7 @@ export default function Header() {
104109
}`}
105110
style={{ zIndex: MOBILE_MENU_LIST }}>
106111
<button
107-
onClick={handleMobileCloseMenuClick}
112+
onClick={hideMobileMenu}
108113
className="absolute top-[2rem] right-[1.5rem] p-[1rem]">
109114
<Image
110115
src={'/icon/button/closeX-white.svg'}
@@ -114,45 +119,35 @@ export default function Header() {
114119
/>
115120
</button>
116121
<nav>
117-
<ul className="font-pp text-[3.2rem] font-[300]">
122+
<ul className="font-pp text-[3.2rem] font-[300] text-white">
118123
<Link
119124
href="/about"
120-
onClick={() => {
121-
setIsShowMobileMenu(false)
122-
}}
123-
className="px-[3rem] py-[1.1rem] block border-b border-white">
125+
onClick={handleMobileLinkClick}
126+
className={getMobileLinkClass('/about')}>
124127
About
125128
</Link>
126129
<Link
127130
href="/people"
128-
onClick={() => {
129-
setIsShowMobileMenu(false)
130-
}}
131-
className="px-[3rem] py-[1.1rem] block border-b border-white">
131+
onClick={handleMobileLinkClick}
132+
className={getMobileLinkClass('/people')}>
132133
People
133134
</Link>
134135
<Link
135-
href="/project "
136-
onClick={() => {
137-
setIsShowMobileMenu(false)
138-
}}
139-
className="px-[3rem] py-[1.1rem] block border-b border-white">
136+
href="/project"
137+
onClick={handleMobileLinkClick}
138+
className={getMobileLinkClass('/project')}>
140139
Project
141140
</Link>
142141
<Link
143142
href="/recruit"
144-
onClick={() => {
145-
setIsShowMobileMenu(false)
146-
}}
147-
className="px-[3rem] py-[1.1rem] block border-b border-white">
143+
onClick={handleMobileLinkClick}
144+
className={getMobileLinkClass('/recruit')}>
148145
Recruit
149146
</Link>
150147
<Link
151148
href="/contact"
152-
onClick={() => {
153-
setIsShowMobileMenu(false)
154-
}}
155-
className="px-[3rem] py-[1.1rem] block border-b border-white">
149+
onClick={handleMobileLinkClick}
150+
className={getMobileLinkClass('/contact')}>
156151
Contact
157152
</Link>
158153
</ul>

src/app/component/Loading.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import React from 'react'
2+
import { motion } from 'framer-motion'
3+
4+
export default function Loading({
5+
loadingProgress
6+
}: {
7+
loadingProgress: number
8+
}) {
9+
const progressNormalized = loadingProgress / 100
10+
const backgroundColor = `rgb(${255 * progressNormalized}, ${255 * progressNormalized}, ${255 * progressNormalized})`
11+
12+
return (
13+
<motion.div
14+
animate={{ y: '0vh' }}
15+
exit={{ y: '100vh' }}
16+
transition={{ duration: 0.5 }}
17+
style={{ backgroundColor }}
18+
className="fixed top-0 left-0 w-screen h-screen flex justify-end items-center pr-[10vw] text-white font-pp font-light text-[8vw] z-[9000]">
19+
<h1 className="mix-blend-difference">{loadingProgress}%</h1>
20+
</motion.div>
21+
)
22+
}

src/hooks/.gitkeep

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/hooks/useLoadingProgress.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { useEffect, useState } from 'react'
2+
//
3+
//
4+
//
5+
const useLoadingProgress = (pathname: string) => {
6+
const [isLoading, setIsLoading] = useState(true)
7+
const [loadingSpeed, setLoadingSpeed] = useState(10)
8+
const [loadingProgress, setLoadingProgress] = useState(0)
9+
10+
const startLoading = () => {
11+
setIsLoading(true)
12+
}
13+
14+
useEffect(() => {
15+
if (isLoading && loadingProgress < 100) {
16+
const timer = setTimeout(() => {
17+
setLoadingProgress(prev => Math.min(prev + 1, 100))
18+
}, loadingSpeed)
19+
return () => clearTimeout(timer)
20+
} else if (loadingProgress === 100) {
21+
setTimeout(() => {
22+
setIsLoading(false)
23+
setLoadingProgress(0)
24+
}, 400)
25+
}
26+
}, [isLoading, loadingProgress, loadingSpeed])
27+
28+
return {
29+
isLoading,
30+
loadingProgress,
31+
setLoadingSpeed,
32+
startLoading
33+
}
34+
}
35+
36+
export default useLoadingProgress

src/hooks/useMobileMenu.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
'use client'
2+
import { useState } from 'react'
3+
import { useLockBodyScroll } from 'react-use'
4+
//
5+
//
6+
//
7+
const useMobileMenu = () => {
8+
const [isShowMobileMenu, setIsShowMobileMenu] = useState(false)
9+
useLockBodyScroll(isShowMobileMenu)
10+
11+
const showMobileMenu = () => {
12+
setIsShowMobileMenu(true)
13+
}
14+
15+
const hideMobileMenu = () => {
16+
setIsShowMobileMenu(false)
17+
}
18+
19+
return {
20+
isShowMobileMenu,
21+
showMobileMenu,
22+
hideMobileMenu
23+
}
24+
}
25+
26+
export default useMobileMenu

src/hooks/useScrollDirection.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use client'
2+
import { useState, useEffect } from 'react'
3+
//
4+
//
5+
//
6+
const useScrollDirection = () => {
7+
const [isShowHeader, setIsShowHeader] = useState(true)
8+
const [lastScrollY, setLastScrollY] = useState(0)
9+
10+
useEffect(() => {
11+
const handleScroll = () => {
12+
const currentScrollY = window.scrollY
13+
if (currentScrollY > lastScrollY) {
14+
setIsShowHeader(false)
15+
} else {
16+
setIsShowHeader(true)
17+
}
18+
setLastScrollY(currentScrollY)
19+
}
20+
window.addEventListener('scroll', handleScroll)
21+
return () => {
22+
window.removeEventListener('scroll', handleScroll)
23+
}
24+
}, [lastScrollY])
25+
26+
return isShowHeader
27+
}
28+
29+
export default useScrollDirection

tailwind.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { HEADER, MOBILE_MENU_LIST } from '@/style/zIndex'
21
import type { Config } from 'tailwindcss'
32

43
const config: Config = {
@@ -72,6 +71,7 @@ const config: Config = {
7271
}
7372
}
7473
},
74+
// eslint-disable-next-line @typescript-eslint/no-require-imports
7575
plugins: [require('tailwind-scrollbar-hide')]
7676
}
7777

0 commit comments

Comments
 (0)