LoginSignup
42
37

More than 3 years have passed since last update.

Next.jsでイケてるハンバーガーメニューを作ろう!

Last updated at Posted at 2021-02-10

対象

  • JSでハンバーガーメニューは作れるけどReactでどうやったらいいかわからない
  • イケてるメニューボタンが作りたい

事前知識

  • html, css, js, React, Next.jsの基礎

準備

環境構築からいきましょう!

npm init next-app menu-button
cd menu-button
mkdir components
cd components
mkdir header
cd header
touch index.jsx
touch index.module.scss

環境構築してcomponentsファイルを作り、headerコンポーネントを作ります。
Sassもインストールしときましょうか

npm install sass

これで準備おっけいです!
今回作るハンバーガーメニューはこちら!!
スクリーンショット 2021-02-10 21.10.04.png

スクリーンショット 2021-02-10 21.10.28.png
スクリーンショット 2021-02-10 21.09.32.png

1024pxからメニューボタンに変わるイメージです!!
ちょっと普通に作るよりカッコよくしてみました。
ハンバーガーメニューってよく多用されてるイメージですが、実はUI的にはよくないと言われてるみたいです。

いろんなサイトのメニューボタンをみて感性を磨きましょう♪

実装

今回はReact hooksのuseStateを使っていきます!!

components/header/index.jsx
import styles from "./index.module.scss";
import Link from "next/link";
import React, {useState} from "react"
export default function Header() {
  const [openMenu, setOpenMenu] = useState(false);
  const menuFunction = () => {
    setOpenMenu(!openMenu);
  }
  return (
    <React.Fragment>
      <header id="header" className={styles.header}>
        <div className={styles.logo}>
          <Link href="/">
            <a className={styles.logo}>ハンバーガー♡</a>
          </Link>
        </div>
        <nav>
          <ul>
            <li>
              <Link href="/">
              <a>メニュー</a>
              </Link>
            </li>
            <li>
              <Link href="/">
              <a>メニュー</a>
              </Link>
            </li>
            <li>
              <Link href="/">
              <a>メニュー</a>
              </Link>
            </li>
            <li>
              <Link href="/">
              <a>メニュー</a>
              </Link>
            </li>
            <li>
              <Link href="/">
              <a>メニュー</a>
              </Link>
            </li>
            <li>
              <Link href="/">
                <a>メニュー</a>
              </Link>
            </li>
          </ul>
        </nav>
        <div className={styles.container}>
          <div className={styles.humburger} onClick={() => menuFunction()}>
            <span className={openMenu ? styles.open : undefined}></span>
            <span className={openMenu ? styles.open : undefined}></span>
            <p className={openMenu ? styles.open : undefined}>Menu</p>
          </div>
        </div>
      </header>
      <div className={`${styles.drawerMenu} ${openMenu ? styles.open : undefined}`}>
        <ul>
          <div className={styles.close} onClick={() => menuFunction()}>
            <span></span>
            <span></span>
            <p>Close</p>
          </div>
          <li>
            <Link href="/">
              <a>
                <p className={styles.mainTitle}>メニュー</p>
                <p className={styles.subTitle}>私のメニュー</p>
              </a>
            </Link>
          </li>
          <li>
            <Link href="/">
            <a>
              <p className={styles.mainTitle}>メニュー</p>
              <p className={styles.subTitle}>私のメニュー</p>
            </a>
            </Link>
          </li>
          <li>
            <Link href="/">
            <a>
              <p className={styles.mainTitle}>メニュー</p>
              <p className={styles.subTitle}>私のメニュー</p>
            </a>
            </Link>
          </li>
          <li>
            <Link href="/">
            <a>
              <p className={styles.mainTitle}>メニュー</p>
              <p className={styles.subTitle}>私のメニュー</p>
            </a>
            </Link>
          </li>
          <li>
            <Link href="/">
            <a>
              <p className={styles.mainTitle}>メニュー</p>
              <p className={styles.subTitle}>私のメニュー</p>
            </a>
            </Link>
          </li>
          <li>
            <Link href="/contact">
            <a>
              <p className={styles.mainTitle}>メニュー</p>
              <p className={styles.subTitle}>私のメニュー</p>
            </a>
            </Link>
          </li>
        </ul>
      </div>
    </React.Fragment>
  )
}

説明していきます!
まず上からコンポーネントを読み込んでいます。
sytelsはscssを読み込んでいるイメージです。
Linkコンポーネントを読み込み
import React, {useState} from "react"でReact hooksのuseStateが読み込めるようになります!

useStateは簡単に説明すると
[変数, 状態を変化させる関数] = useState(初期値)
って感じです。
一般的に関数はset変数名で書くのを見かけることが多いです!!

この場合は初期値がfalseになっているのでこれを状態繊維させたいときは
setOpenMenu(true)って感じですね!
これでfalseからtrueになります!!

つまりその下に書いてある関数menuFunctionの
setOpenMenu(!openMenu)はtrueとfalseを繊維させる意味をしています!
先頭に!をつけると簡単にいうと"〜じゃない"って感じになります。
trueじゃない = false
falseじゃない = true
って感じですね!!

ハンバーガーメニューは開いてるか(true)閉じているか(false)しかないので、trueとfalseの真偽知で示すことができます!
初めの状態は閉じていて欲しいのでfalseを初期値にします!

あとはメニューが開いている状態を.openクラスに渡して、trueならばクラスを渡すと言った記述をします!

  <div className={styles.humburger} onClick={() => menuFunction()}>
     <span className={openMenu ? styles.open : undefined}></span>
     <span className={openMenu ? styles.open : undefined}></span>
     <p className={openMenu ? styles.open : undefined}>Menu</p>
  </div>

クリックイベントmenuFunctionを発火させ、
openMenu ? styles.open : undefindは
三項演算子です!わからない方はぐぐりましょう!
openMenuがfalseのときはクラスを何も渡さないのでundefindを渡しています!!

スタイリングしましょう!!

components/header/index.module.scss
  padding: 0 40px;
  z-index: 50;
  position: fixed;
  .logo {
    font-size: 20px;
  }

  nav {
    margin-left: auto;
  }
  li {
    display: inline-block;
    margin-right: 15px;

    &:nth-child(6) {
      background-color: rgb(34, 35, 35);
      color: white;
      width: 90px;
      padding: 5px 10px;
      margin-right: 0;
    }
  }
}

@media (max-width: 1024px) {
  .header {
    position: initial;
    nav {
      display: none;
    }
  }
}

.humburger {
  background-color: rgb(34, 35, 36);
  color: white;
  padding: 10px 5px 0 5px;
  display: none;
  cursor: pointer;
  position: fixed;
  top: 20px;
  right: 40px;
  span {
    background-color: white;
    width: 30px;
    height: 1px;
    display: block;
    margin-bottom: 7px;
    &:nth-child(1) {
      width: 20px;
      margin-left: 10px;
    }
  }
  p {
    font-size: 13px;
    margin-top: -4px;
    margin-bottom: 5px;
  }
}
.drawerMenu {
  width: 0;
  color: white;
  background-color: rgb(34, 35, 36);

  transition: 0.5s;
  height: 0;
  opacity: 0;
  z-index: -100;
  &.open {
    height: 100vh;
    opacity: 1;
    z-index: 100;
  }
}

@media (max-width: 1024px) {
  .header {
    padding: 0 20px;
    .logo {
      margin-top: 25px;
    }
  }
  .container {
    margin: 0 0 0 auto;
    .humburger {
      display: block;
    }
  }
  .drawerMenu {
    width: 100%;
    position: fixed;
    top: 0;
    ul {
      width: 90%;
      margin: 0 auto;
      li {
        margin-bottom: 25px;
        opacity: 0.8;
        font-family: "Josefin Sans", sans-serif;
        font-weight: 200;
      }
    }
  }
  .mobileLogo {
    width: 250px;
    height: 80px;
    display: inline-block;
    position: absolute;
    top: 0px;
  }
  .mainTitle {
    font-size: 28px;
    margin-bottom: 5px;
    letter-spacing: 2px;
  }
  .subTitle {
    font-size: 15px;
  }
  .close {
    cursor: pointer;
    margin-top: 28px;
    padding-right: 5px;
    span {
      background-color: white;
      width: 30px;
      height: 1px;
      display: block;
      margin-bottom: 10px;
      margin: 0 0 10px auto;
      &:first-child {
        transform: translateY(6px) rotate(150deg);
      }
      &:nth-child(2) {
        transform: translateY(-6px) rotate(-150deg);
        margin-bottom: 0;
      }
    }
    p {
      text-align: right;
      font-size: 11px;
      padding-right: 1px;
    }
  }
}
@media (max-width: 600px) {
  .header {
    padding: 0 20px;
  }
  .humburger {
    top: 15px;
    right: 20px;
  }
  .mobileLogo {
    width: 190px;
    height: 60px;
    top: 10px;
  }
}
@media (max-width: 340px) {
  .header {
    padding: 0 10px;
    .logo {
      width: 170px;
      height: 60px;
    }
  }
}

.drawerMenu {
width: 0;
color: white;
background-color: rgb(34, 35, 36);

transition: 0.5s;
height: 0;
opacity: 0;
z-index: -100;
&.open {
height: 100vh;
opacity: 1;
z-index: 100;
}
}
ここが重要です。
drowerMenuを閉じている状態では、widthとheightとopacityを0にして見えない状態にします!
クリックイベントがされると
heightが100vhに
100vhは簡単にいうと画面いっぱいにって感じです。
opacityとz-indexが付与されて
表示されてきます♪
transitionをつけると少しアニメーションをつけることができます!!

お疲れ様でした!!

こちら参考のgithubのコードです。
https://github.com/abeshi03/menu-botton-next/tree/main/components/header

42
37
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
42
37