CSS & JS Effect – Hamburger Menu
效果
参考:
Youtube – Responsive Navigation Menu Bar + Hamburger Menu Toggle - Only with CSS
Youtube – Making an animated hamburger button and a challenge to you!
Youtube – JavaScript - How to Create a Responsive Hamburger Menu with HTML, CSS, & JavaScript
做法
一条一条的 bar 是 div / span 做的.
点击的时候, 中间的 bar 消失, 上下的 bar rotate 就形成了 X
HTML
<div class="nav"> <input class="hamburger-toggle" type="checkbox" /> <span class="hamburger"></span> </div>
checkbox 用来做 toggle, 其实用 JS 做会比较容易. checkbox 的 size 不容易控制, 又要搞定位什么的, 很不直观.
hamburger bar 虽然有三条, 但上下 2 条是用 ::before ::after 搞出来的. 其实要用 3 个 span 也是 ok 的, 甚至更直观.
CSS Style
3 bar style
.hamburger, .hamburger::before, .hamburger::after { background-color: black; width: 2rem; height: 0.25rem; display: inline-block; transition-property: transform top opacity; transition-duration: 0.4s; }
3 条 bar 一起画.
transition 是等下 toggle 时 rotate 和 opacity 的过渡效果.
top, bottom bar style
.hamburger { position: relative; &::before, &::after { content: ""; position: absolute; } &::before { top: -0.5rem; } &::after { top: 0.5rem; } }
这里要注意, 3 条 bar 并不是 sibling. 它是 parent child
但是眼睛看上去应该要是 before, hamburger, after sibling才对.
所以就需要用到绝对定位. 感觉很不直观...
通过绝对定位, 它们就重叠了, 然后 before top -0.5rem 往上, after top 0.5rem 往下, 最终就形成 3 条 bar 了.
checkbox style
.nav { position: relative; .hamburger-toggle { position: absolute; top: 0px; left: 0px; width: 2rem; height: 100%; z-index: 1; opacity: 0; cursor: pointer; } }
它也是绝对定位, 因为要和 3 条 bar 重叠, 这样才点击的到, 用 opacity 让它消失 (看不见但是点击的到)
toggle checked style
.hamburger-toggle { &:checked ~ .hamburger { background-color: transparent; &::before { transform: rotate(45deg); top: 0; } &::after { transform: rotate(-45deg); top: 0; } } }
checked 时让中间的 bar, 也就是 .hamburger background-color transparent 消失. 这里不可以用 opacity 哦.
因为 opacity 会同时让它的 child (before, after) 一起消失, 这不是想要的.
before 和 after 就 rotate 45°, 一个往上, 一个往下, 默认的 rotate origin (轴心) 是 center 所以必须加上 top 0 调回正中间.
如果没有 top: 0 效果是下面这样.
红色是原来的位置, 黑色是 rotate 以后的位置. 当把 2 个 set 成 top:0, 上面的 bar 往下移, 下面的往上移, 2 个 bar 在中间交会就形成了 X.
注: rotate 以后 width 就比原本的短了, 所以左边会有一点点偏移, 但是看不太出来, 所以不用理会.
完整代码
HTML

<div class="nav"> <input class="hamburger-toggle" type="checkbox" /> <span class="hamburger"></span> </div>
Sass

.nav { position: relative; .hamburger-toggle { position: absolute; top: 0px; left: 0px; width: 2rem; height: 100%; z-index: 1; opacity: 0; cursor: pointer; &:checked ~ .hamburger { background-color: transparent; &::before { transform: rotate(45deg); top: 0; } &::after { transform: rotate(-45deg); top: 0; } } } .hamburger, .hamburger::before, .hamburger::after { background-color: black; width: 2rem; height: 0.25rem; display: inline-block; transition-property: transform top opacity; transition-duration: 0.4s; } .hamburger { position: relative; &::before, &::after { content: ""; position: absolute; } &::before { top: -0.5rem; } &::after { top: 0.5rem; } } }
直观版本
如果不喜欢搞一堆的定位, before after, 也可以用最简单的 way.
HTML
<button class="menu-btn"> <span class="top-bar"></span> <span class="center-bar"></span> <span class="bottom-bar"></span> </button>
CSS Style
.menu-btn { --bar-gap: 0.75rem; width: 100px; height: 100px; display: flex; flex-direction: column; justify-content: center; gap: var(--bar-gap); .top-bar, .center-bar, .bottom-bar { background-color: currentColor; height: 1rem; width: 100%; border-radius: 999px; transition-property: transform opacity; transition-duration: 0.4s; } &.opened { .center-bar { opacity: 0; } .top-bar { transform: translateY(calc(var(--bar-gap) + 100%)) rotate(45deg); } .bottom-bar { transform: translateY(calc(-1 * (var(--bar-gap) + 100%))) rotate(-45deg); } } }
需要留意它的 transform 还加上了 translateY 桥位置.
JS
document.querySelector('.menu-btn').addEventListener('click', event => { event.currentTarget.classList.toggle('opened'); });