[WebUI]Tailwind + nextj(Hamburger+Slide Menu) – 7

πŸ‘‰πŸ» 햄버거 메뉴와 μŠ¬λΌμ΄λ“œ 메뉴λ₯Ό κ²°ν•©ν•œ ν˜•νƒœμ˜ λ©”λ‰΄μž…λ‹ˆλ‹€.
This is a menu format that combines a hamburger menu with a slide-out menu.

πŸ‘‰πŸ» 햄버거 메뉴λ₯Ό ν΄λ¦­ν•˜λ©΄ μŠ¬λΌμ΄λ“œλ©”λ‰΄κ°€ λ‚˜νƒ€λ‚©λ‹ˆλ‹€.
Clicking the hamburger menu reveals a slide-out menu.

πŸ‘‰πŸ» μŠ¬λΌμ΄λ“œλ©”λ‰΄λŠ” μš°μΈ‘λ˜λŠ” μ’ŒμΈ‘μ—μ„œ λ‚˜νƒ€λ‚˜κ²Œ μ„€μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
The slide menu can be set to appear from either the right or the left side.

πŸ‘‰πŸ» λΆ€λ“œλŸ¬μš΄ 메뉴열림 λ‹«νž˜μ„ μœ„ν•΄μ„œ css와 tailwindcssλ₯Ό ν˜Όν•©ν•΄μ„œ μ‚¬μš©ν•©λ‹ˆλ‹€.
To ensure smooth opening and closing of the menu, a combination of CSS and Tailwind CSS is used.

πŸ“ 디렉토리 ꡬ쑰 / Directory structure

tailwindcss-nextjs/
└── app/
    β”œβ”€β”€ layout.tsx
    β”œβ”€β”€ page.tsx (루트 /)
    β”œβ”€β”€ globals.css
    β”œβ”€β”€ favicon.ico
    β”‚
    β”œβ”€β”€ ui/                             
    β”‚   β”œβ”€β”€ page.tsx                   
    β”‚   └── _components/
    β”‚       β”œβ”€β”€ button.tsx
    β”‚       β”œβ”€β”€ dropdown.tsx
    β”‚       └── navigation/
    β”‚           β”œβ”€β”€ HorizontalNavMenu.tsx  
    β”‚           β”œβ”€β”€ MegaMenu.tsx          
    β”‚           └── Hamburger-Drawer.tsx  # hamberger-drawer
    β”‚
    β”œβ”€β”€ hamberger-drawer/         
    β”‚   β”œβ”€β”€ page.tsx                     # /hamberger-drawer
    β”‚   └── game/
    β”‚       └── page.tsx                 # /hamberger-drawer/game
    β”‚
    └── button-css/ , button-tailwind/ ... ...

πŸ“ μŠ¬λΌμ΄λ“œλ©”λ‰΄μ„€μ • / Slide Menu Settings

βœ”οΈ μŠ¬λΌμ΄λ“œλ©”λ‰΄λ₯Ό 였λ₯Έμͺ½ λ˜λŠ” μ™Όμͺ½μ—μ„œ λ‚˜μ˜€κ²Œ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
You can have the slide menu appear from either the right or the left.

1)였λ₯Έμͺ½μ—μ„œ λ‚˜μ˜€κ²Œ μ„€μ • / Set it to appear from the right

... ...
          {/** 였λ₯Έμͺ½μ—μ„œ 메뉴 λ‚˜μ˜€λŠ” μ„€μ • / Setting to make the menu appear from the right */}
        <div className={`fixed top-0 right-0 h-full w-[300px] bg-white z-50 shadow-2xl flex flex-col ${open ? 'animate-slide-in' : 'animate-slide-out'}`}>
          
... ...
 <style>{`
        /* λ“€μ–΄κ°ˆ λ•Œ - 였λ₯Έμͺ½ / When entering from the right */
        @keyframes slide-in {
          from { transform: translateX(100%); }
          to { transform: translateX(0); }
        }
... ...
        /* λ‚˜κ°ˆ λ•Œ-였λ₯Έμͺ½ / When exiting to the right */
        @keyframes slide-out {
          from { transform: translateX(0); }
          to { transform: translateX(100%); }
        }
... ...
`}</style>
... ...

2)μ™Όμͺ½μ—μ„œ λ‚˜μ˜€κ²Œ μ„€μ • / Set it to appear from the left

... ...
{/* μ™Όμͺ½μ—μ„œ 메뉴 λ‚˜μ˜€λŠ” μ„€μ • / Setting to make the menu appear from the left */}
<div className={`fixed top-0 left-0 h-full w-[300px] bg-white z-50 shadow-2xl flex flex-col ${open ? 'animate-slide-in' : 'animate-slide-out'}`}> 

... ...
      <style>{`
... ...

        /* λ“€μ–΄κ°ˆ λ•Œ - μ™Όμͺ½ / When entering from the left */
        /*@keyframes slide-in {
           from { transform: translateX(-100%); }
           to { transform: translateX(0); }
        }*/
... ...
        /* λ‚˜κ°ˆ λ•Œ-μ™Όμͺ½ / When exiting to the left */
       /* @keyframes slide-out {
          from { transform: translateX(0); }
          to { transform: translateX(-100%); }
        } */
      `}</style>
... ...

3)top-0은 μœ„μ—μ„œ 0px만큼 μœ„μΉ˜ ,right-0λŠ” 였λ₯Έμͺ½μ—μ„œ 0px만큼 μœ„μΉ˜,left-0λŠ” μ™Όμͺ½μ—μ„œ 0px만큼 μœ„μΉ˜ ν•˜λ„λ‘ μ„€μ •
top-0 sets the position to 0px from the top, right-0 sets it to 0px from the right, and left-0 sets it to 0px from the left.

πŸ“ 메뉴 λžœλ”λ§ μƒνƒœ / Menu rendering state

... ...
const [render, setRender] = useState(false) 
... ...
  useEffect(() => {
    if (open) {
      setRender(true)
      document.body.style.overflow = 'hidden'
    } else {
      // 메뉴 λ‹«μ„λ•Œ , 0.3초 후에 setRenderν•¨μˆ˜ μ‹€ν–‰ 
      // When closing the menu, execute the `setRender` function after 0.3 seconds.
      const t = setTimeout(() => setRender(false), 300)
      document.body.style.overflow = ''
      return () => clearTimeout(t)
    }
  }, [open])
... ...

1)메뉴가 열리고 λ‹«νž˜μ— 따라 setRenderν•¨μˆ˜λ‘œ 메뉴 λžœλ”λ§ μƒνƒœλ₯Ό μ²˜λ¦¬ν•©λ‹ˆλ‹€.
The rendering state of the menu is managed using the setRender function as the menu opens and closes.

2)메뉴가 μ˜€ν”ˆ λ˜μ—ˆμ„λ•ŒλŠ” 반투λͺ… λ°°κ²½λ’€μ˜ ν™”λ©΄ μŠ€ν¬λ‘€μ„ μˆ¨κΉ€μ²˜λ¦¬ν•˜κ³  메뉴가 λ‹«νžˆλ©΄ 슀크둀 μ‚¬μš©κ°€λŠ₯ν•˜κ²Œ μ„€μ •ν•©λ‹ˆλ‹€.
When the menu is opened, scrolling on the screen behind the semi-transparent background is disabled; when the menu is closed, scrolling is re-enabled.

# 메뉴가 열림인 경우 / When the menu is open
document.body.style.overflow = 'hidden'

# 메뉴가 λ‹«νž˜μΈ 경우 / When the menu is closed
document.body.style.overflow = ''

πŸ“ 햄버거 메뉴 μ—΄λ¦Ό/λ‹«νž˜ λ²„νŠΌ
Hamburger menu open/close button

... ... 
<button onClick={() => setOpen(true)} className="w-11 h-11 bg-black text-white rounded-full">☰</button>
... ...
<button onClick={() => setOpen(false)} className="w-9 h-9 bg-gray-100 rounded-full">βœ•</button>
... ...

πŸ“ μŠ¬λΌμ΄λ“œ 메뉴 μ—΄λ¦Ό/λ‹«νž˜
Open/close slide menu

... ...
{render && ( ... 
...)}
... ...

πŸ“ μŠ€νƒ€μΌ /

βœ”οΈ μŠ¬λΌμ΄λ“œ 메뉴 μˆ¨κΉ€,λ‚˜νƒ€λ‚˜κΈ°
Hide/show slide menu

... ... 
  from { transform: translateX(100%); } // μ‹œμž‘ / start
  to { transform: translateX(0); } // 끝 / end
... ...

1)μŠ¬λΌμ΄λ“œ 메뉴가 μ‹œμž‘ μœ„μΉ˜ λΆ€ν„° 끝 μœ„μΉ˜κΉŒμ§€ μ΄λ™ν•©λ‹ˆλ‹€.
The slide menu moves from the starting position to the end position.

2)100%λŠ” μŠ¬λΌμ΄λ“œ λ©”λ‰΄μ˜ 전체 ν¬κΈ°μž…λ‹ˆλ‹€.
100% is the full size of the slide menu.

3)즉 메뉴 μˆ¨κ²¨μ‘Œλ‹€κ°€ 메뉴 전체가 0pxλ–¨μ–΄μ§„ μœ„μΉ˜λ§ŒνΌ μ΄λ™ν•˜λŠ” μ„€μ •μž…λ‹ˆλ‹€.
In other words, this setting involves the menu starting out hidden and then shifting by a distance of 0px.

4)λ©”λ‰΄μ˜ λ°©ν–₯은 divμ½”λ“œμ—μ„œ right-0λ˜λŠ” left-0κ°€ 초기 λ©”λ‰΄μ˜ μœ„μΉ˜μž…λ‹ˆλ‹€. 이 λΆ€λΆ„μ—μ„œ 메뉴 이동 μ• λ‹ˆλ©”μ΄μ…˜μ΄ μ‹œμž‘λ©λ‹ˆλ‹€.
The direction of the menu is determined by the right-0 or left-0 setting in the div code, which defines the initial position of the menu. The animation for the menu’s movement begins from this point.

<div className={`fixed top-0 right-0 h-full w-[300px] ... ...${open ? 'animate-slide-in' : 'animate-slide-out'}`}>
... ...
</div>
          

βœ”οΈ μ• λ‹ˆλ©”μ΄μ…˜ ν˜•νƒœ / Animation format

... ...
        @keyframes fade-in {
          from { opacity: 0; }
          to { opacity: 1; }
        }
... ...
        @keyframes fade-out {
          from { opacity: 1; }
          to { opacity: 0; }
        }
... ...

1)νŽ˜μ΄λ“œ 인(천천히 λ‚˜νƒ€λ‚¨),νŽ˜μ΄λ“œ 아웃(천천히 사라짐) μ„€μ •μž…λ‹ˆλ‹€.
These are the settings for fade-in (appearing gradually) and fade-out (disappearing gradually).

2)opacityκ°€ 0이면 투λͺ…,1이면 뢈투λͺ…이고 0.5이면 반투λͺ…μž…λ‹ˆλ‹€.
An opacity value of 0 is transparent, 1 is opaque, and 0.5 is semi-transparent.

... ...
        .animate-slide-in { animation: slide-in 0.3s ease-out forwards; }
        .animate-slide-out { animation: slide-out 0.3s ease-out forwards; }
        .animate-fade-in { animation: fade-in 0.3s ease-out forwards; }
        .animate-fade-out { animation: fade-out 0.3s ease-out forwards; }
... ...

1)μŠ¬λΌμ΄λ“œμΈ,μŠ¬λΌμ΄λ“œμ•„μ›ƒ λ˜λŠ” νŽ˜μ΄λ“œμΈ,νŽ˜μ΄λ“œμ•„μ›ƒν• λ•Œ μ„ΈλΆ€μ„€μ •μž…λ‹ˆλ‹€.
These are the detailed settings for slide-in, slide-out, fade-in, or fade-out effects.

2)0.3sλŠ” μ• λ‹ˆλ©”μ΄μ…˜μ†λ„,ease-out은 μ• λ‹ˆλ©”μ΄μ…˜ κ΅¬ν˜„μ€‘ μ†λ„ν˜•νƒœ,forwardλŠ” μ• λ‹ˆλ©”μ΄μ…˜ λλ‚œ ν›„μ˜ μƒνƒœμž…λ‹ˆλ‹€.
‘0.3s’ is the animation duration, ‘ease-out’ is the speed pattern during the animation, and ‘forwards’ defines the state after the animation has finished.

3)ease-out은 μ²˜μŒμ—” λΉ λ₯΄κ²Œ, λμ—μ„œ 천천히 μ‹€ν–‰λ©λ‹ˆλ‹€.이 외에도 ease-in,ease-in-out,linear등이 μžˆμŠ΅λ‹ˆλ‹€.
‘ease-out’ starts quickly and slows down toward the end. Other options include ‘ease-in’, ‘ease-in-out’, and ‘linear’.

4)forwards섀정이 μ—†μœΌλ©΄ μ• λ‹ˆλ©”μ΄μ…˜μ΄ λλ‚œν›„ 처음으둜 λŒμ•„κ°‘λ‹ˆλ‹€. μ• λ‹ˆλ©”μ΄μ…˜μ΄ λλ‚œν›„ 메뉴가 μ—΄λ¦Όμƒνƒœλ‘œ μœ μ§€ν•˜λ €λ©΄ forwards 섀정이 μžˆμ–΄μ•Ό ν•©λ‹ˆλ‹€.
If the ‘forwards’ setting is not used, the animation will return to its initial state after it finishes.
To keep the menu in an open state after the animation finishes, you must include the forwards setting.

πŸ“ ν…ŒμŠ€νŠΈ / Test

βœ”οΈ PC

βœ”οΈ Mobile

λ§ˆνƒœλ³΅μŒ 21μž₯ 22절 / Matthew 21: 22

“λ„ˆν¬κ°€ 기도할 λ•Œμ— 무엇이든지 λ―Ώκ³  κ΅¬ν•˜λŠ” 것은 λ‹€ λ°›μœΌλ¦¬λΌ ν•˜μ‹œλ‹ˆλΌ”

“And everything you ask in prayer, believing, you will receive.”

Leave a Reply