[react] μ€ν¬λ‘€μ λ°λΌ λνλκ³ μ¬λΌμ§λ λ€λΉκ²μ΄μ λ° κ΅¬ννκΈ° (Navbar)
νλ‘κ·Έλλ° μΈκ³λ₯Ό νꡬν©μλ€.
μ€ν¬λ‘€μ λ°λΌ λνλκ³ μ¬λΌμ§λ λ€λΉκ²μ΄μ λ° κ΅¬ννκΈ°
μ΄λ² νλ‘μ νΈμμ μ€ν¬λ‘€μ λ°λΌ λνλκ³ μ¬λΌμ§λ λ€λΉκ²μ΄μ λ°λ₯Ό ꡬννμ΅λλ€.
μ€ν¬λ‘€μ λ΄λ¦΄ λλ μ¬λΌμ‘λ€κ°, μ€ν¬λ‘€μ μ¬λ¦¬λ©΄ λ€μ λνλ©λλ€.
ꡬν νλ©΄
μμ± μ½λ
λ§μ§λ§ μ€ν¬λ‘€ μμΉλ₯Ό κΈ°μ΅νμ¬ λ€λΉκ²μ΄μ λ°μ νμ μνλ₯Ό λ³κ²½νλ μ½λμ λλ€.
μ€ν¬λ‘€μ΄ μλλ‘ μ§ν μ€μ΄λ©΄ false, μ€ν¬λ‘€μ΄ μλ‘ μ§ν μ€μ΄λ©΄ trueλ‘ μ€μ νμ¬ λ° μνλ₯Ό λ³κ²½νμμ΅λλ€.
Nav.jsx
import { useEffect, useState } from 'react';
import style from './Nav.module.css'
import { Link } from 'react-router-dom';
function Nav() {
// λ€λΉκ²μ΄μ
λ° νμ μνλ₯Ό κ΄λ¦¬νλ state
const [showNav, setShowNav] = useState(true);
// λ§μ§λ§ μ€ν¬λ‘€ μμΉλ₯Ό μ μ₯νλ state
const [lastScrollY, setLastScrollY] = useState(0);
useEffect(() => {
// μ€ν¬λ‘€ μ΄λ²€νΈλ₯Ό μ²λ¦¬νλ ν¨μ
const handleScroll = () => {
const currentScrollY = window.scrollY;
// μ€ν¬λ‘€μ΄ μλλ‘ μ§νλμλμ§ νμΈ
if (currentScrollY > lastScrollY) {
setShowNav(false); // μ€ν¬λ‘€μ λ΄λ¦΄ λ λ€λΉκ²μ΄μ
λ° μ¨κΈ°κΈ°
} else {
setShowNav(true); // μ€ν¬λ‘€μ μ¬λ¦΄ λ λ€λΉκ²μ΄μ
λ° νμ
}
// λ§μ§λ§ μ€ν¬λ‘€ μμΉ μ
λ°μ΄νΈ
setLastScrollY(currentScrollY);
};
// μ€ν¬λ‘€ μ΄λ²€νΈ 리μ€λ λ±λ‘
window.addEventListener('scroll', handleScroll);
// ν΄λ¦°μ
ν¨μμμ μ΄λ²€νΈ 리μ€λ μ κ±°
return () => window.removeEventListener('scroll', handleScroll);
}, [lastScrollY]); // lastScrollYκ° λ³κ²½λ λλ§λ€ useEffectκ° μ€νλ©λλ€.
return (
<div className={`${style.header} ${showNav ? '' : style.hideNav}`}>
<Link className={style.Logo} to="/">
<img src={`${process.env.PUBLIC_URL}/img/logo.svg`} alt="MDS Logo" className={style.logo} />
</Link>
<div className={style.navbar}>
<Link className={style.navbarMenu} to={'/map'}>
<img src={`${process.env.PUBLIC_URL}/img/mountainpath.svg`} alt="λ±μ° μ 보 μμ΄μ½" className={style.icon1} />λ±μ° μ 보
</Link>
<Link className={style.navbarMenu} to={'/review'}>
<img src={`${process.env.PUBLIC_URL}/img/flag.svg`} alt="λ±μ° νκΈ° μμ΄μ½" className={style.icon2} />λ±μ° νκΈ°
</Link>
<Link className={style.navbarMenu} to={'/rec'}>
<img src={`${process.env.PUBLIC_URL}/img/mountainrec.svg`} alt="λ±μ°λ‘ μΆμ² μμ΄μ½" className={style.icon1} />λ±μ°λ‘ μΆμ²λ°κΈ°
</Link>
</div>
</div>
)
}
export default Nav;
Nav.module.css
.hidenav μ€νμΌμμ navbarλ₯Ό μ¨κΈ°λ ν¨κ³Όλ₯Ό μ μ©ν©λλ€.
.header {
display: flex;
align-items: center;
background-color: rgba(179, 179, 179, 0.42);
padding-left: 2rem;
z-index: 1000;
/* λ€λ₯Έ μμλ€ μμ μ€λλ‘ z-index μ€μ */
position: fixed;
transition: transform 0.3s ease;
/* transform μμ±μ λν μ ν ν¨κ³Ό */
}
.logo {
width: 12rem;
height: 5rem;
}
.icon1 {
width: 3rem;
height: 1.5rem;
}
.icon2 {
width: 2rem;
height: 1.1rem;
}
.navbar {
justify-content: center;
width: 100vw;
text-align: center;
font-size: 1.2rem;
}
.navbarMenu {
margin: 5rem;
text-decoration: none;
color: rgb(0, 0, 0);
transition: color 0.3s ease;
/* μμ λ³νμ μ λλ©μ΄μ
ν¨κ³Όλ₯Ό μΆκ°ν©λλ€ */
position: relative;
/* κ°μ μμμ κΈ°μ€μ μ€μ */
overflow: hidden;
/* λ°μ€μ΄ λ§ν¬ μμ λ°κΉ₯μΌλ‘ λκ°μ§ μλλ‘ μ€μ */
}
.navbarMenu:hover {
color: #3ca50f;
/* λ§μ°μ€ μ€λ² μ κΈμ μμμ λ³κ²½ν©λλ€ */
}
.navbarMenu::after {
content: '';
/* κ°μ μμμ λ΄μ©μ λΉμλ‘λλ€ */
position: absolute;
left: 0%;
/* μΌμͺ½μμ 50%μ μμΉμμ μμ */
bottom: -0.5rem;
/* λ§ν¬ λ°λ₯μΌλ‘λΆν° -0.5rem μμ μμΉ */
width: 0;
/* μ΄κΈ° λλΉλ 0μΌλ‘ μ€μ νμ¬ λ³΄μ΄μ§ μκ² ν©λλ€ */
height: 0.13rem;
/* λ°μ€μ λκ» */
background: rgba(0, 0, 0, 0.279);
/* λ°μ€μ μμ */
transition: width 0.5s ease, left 0.5s ease;
/* λλΉμ μμΉμ λν μ ν ν¨κ³Ό μ€μ */
}
.navbarMenu:hover::after,
.navbarMenu:focus::after {
width: 100%;
/* λ§μ°μ€λ₯Ό μ¬λ¦¬κ±°λ ν¬μ»€μ€ μ λ°μ€μ λλΉλ₯Ό 100%λ‘ λ³κ²½ */
left: 0;
/* μμΉλ₯Ό μΌμͺ½ λμΌλ‘ λ³κ²½ */
}
/* Nav.module.css */
.hideNav {
transform: translateY(-100%);
/* λ€λΉκ²μ΄μ
λ°λ₯Ό μλ‘ μ¨κΉλλ€ */
transition: transform 0.3s ease;
/* λΆλλ¬μ΄ μ ν ν¨κ³Ό */
}
μμ κ°μ λ°©μμΌλ‘ μ€ν¬λ‘€μ λ°λΌ μ¬λΌμ§κ³ λνλλ λ€λΉκ²μ΄μ λ°λ₯Ό ꡬνν μ μμ΅λλ€.