zl程序教程

您现在的位置是:首页 >  其他

当前栏目

CSS & JS Effect – Hamburger Menu

ampJSCSS Menu Effect
2023-09-27 14:23:55 时间

效果

 

参考:

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>
View Code

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;
    }
  }
}
View Code

 

直观版本

如果不喜欢搞一堆的定位, 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');
});