対象
- 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
これで準備おっけいです!
今回作るハンバーガーメニューはこちら!!
1024pxからメニューボタンに変わるイメージです!!
ちょっと普通に作るよりカッコよくしてみました。
ハンバーガーメニューってよく多用されてるイメージですが、実はUI的にはよくないと言われてるみたいです。
いろんなサイトのメニューボタンをみて感性を磨きましょう♪
実装
今回はReact hooksのuseStateを使っていきます!!
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を渡しています!!
スタイリングしましょう!!
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