Nuxt.jsでレスポンシブのメニューを作成する方法

Vue.js

Nuxt.jsでレスポンシブのメニューを作成する方法を紹介します。

Nuxt.jsでレスポンシブのメニューを作成する方法

作成イメージ

ディレクトリ構造


root
├─ src
   ├─ components
   │   ├─ Navigation
   │   │   ├─ TheHeader.vue
   │   │   ├─ TheSidenav.vue
   │   │   └─ TheSideNavToggle.vue
   ├─ layouts
   │   ├─ default.vue
   ├─ pages
   │   ├─ about
   │   │   ├─ index.vue
   │   ├─ admin
   │   │   ├─ index.vue
   │   ├─ posts
   │   │   ├─ index.vue

手順

  1. Navigationのコンポーネントを作成
  2. レスポンシブメニューを表示させたいレイアウトにNavigationのコンポーネントを記述する
  3. styleではnuxt-link-activeクラスを付与する

Navigationのコンポーネントは、ヘッダー部分(TheHeader.vue)とサイドバーを表示非表示に切り替えるハンバーガー部分(TheSideNavToggle.vue)、そしてサイドバー部分(TheSidenav.vue)を作成します。

TheHeader.vue

<template>
<div class="header-container">
  <header class="the-header">
    <!-- ハンバーガー(768px以上で非表示) -->
    <TheSideNavToggle @toggle="$emit('sidenavToggle')" />
    <div class="logo">
      <nuxt-link to="/">タイトル</nuxt-link>
    </div>
    <!-- スペース -->
    <div class="spacer"></div>
    <!-- ナブアイテム(768px未満で非表示) -->
    <div class="navigation-items">
      <ul class="nav-list">
        <li class="nav-item"><nuxt-link to="/posts">ブログ</nuxt-link></li>
        <li class="nav-item"><nuxt-link to="/about">アバウト</nuxt-link></li>
        <li class="nav-item"><nuxt-link to="/admin">管理画面</nuxt-link></li>
      </ul>
    </div>
  </header>
</div>
</template>

templateでは、ハンバーガー、タイトル、ナブアイテムをセットします。
ハンバーガーとナブアイテムはメディアクエリで表示を切り替えます。

<script>

import TheSideNavToggle from "@/components/Navigation/TheSideNavToggle";

export default {
  name: "TheHeader",
  components: {
    TheSideNavToggle
  }
};</script>

サイドバーの切替には、トグルできるコンポーネントを作成します。

<style scoped>

.header-container {
  height: 60px;
}

.the-header {
  width: 100%;
  position: fixed;
  height: 60px;
  display: flex;
  justify-content: space-around;
  align-items: center;
  background: linear-gradient(to right, #0f2027, #203a43, #2c5364);
  z-index: 100;
  box-sizing: border-box;
  padding: 0 20px;
}

.logo {
  margin: 0 10px;
  font-size: 1.3rem;
}

.logo a {
  text-decoration: none;
  color: white;
}

.spacer {
  flex: 1;
}

.navigation-items {
  display: none;
}

@media (min-width: 768px) {
  .navigation-items {
    display: block;
  }
}

.nav-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
}

.nav-item {
  margin: 0 10px;
}

.nav-item a {
  text-decoration: none;
  color: white;
}

/* nuxt-link-activeでアクティブクラスをハイライト */
.nav-item a:hover,
.nav-item a:active,
.nav-item a.nuxt-link-active {
  color: #f7797d;
}</style>

現在表示されているリンクに色をつけたり特定のスタイルを適用させたい場合、
nuxt-link-activeクラスを付与することで実現できます。

TheSidenav.vue

<template>
<div class="sidenav-container">
  <!-- サイドバーのバックドロップ背景 -->
  <div
    v-if="show"
    class="sidenav-backdrop"
    @click="$emit('close')"></div>
  <!-- トランジション -->
  <transition name="slide-side">
    <!-- サイドナブ -->
    <div
      v-if="show"
      class="sidenav">
      <div
        class="drawer-toggle"
        role="button"
        @click="$emit('close')">
        <div class="bar"></div>
        <div class="bar"></div>
      </div>
      <!-- ハンバーガーメニュー -->
      <ul
        class="nav-list"
        @click="$emit('close')">
        <li class="nav-item"><nuxt-link to="/posts">ブログ</nuxt-link></li>
        <li class="nav-item"><nuxt-link to="/about">About</nuxt-link></li>
        <li class="nav-item"><nuxt-link to="/admin">管理画面</nuxt-link></li>
      </ul>
    </div>
  </transition>
</div>
</template>
<script>

export default {
  name: "TheSidenav",
  props: {
    show: {
      type: Boolean,
      default: false
    }
  }
}</script>
<style scoped>

.sidenav-container {
  height: 100%;
  width: 100%;
}

.sidenav-backdrop {
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.7);
  z-index: 1000;
  position: fixed;
  top: 0;
  left: 0;
}

.sidenav {
  height: 100%;
  width: 300px;
  background-color: white;
  z-index: 10000;
  position: fixed;
  top: 0;
  left: 0;
  box-sizing: border-box;
  padding: 30px;
}

.slide-side-enter-active,
.slide-side-leave-active {
  transition: all 0.3s ease-out;
}
.slide-side-enter,
.slide-side-leave-to {
  transform: translateX(-100%);
}

.nav-list {
  list-style: none;
  padding: 0;
  margin: 0;
}

.nav-item {
  margin: 20px 0;
}

.nav-item a {
  text-decoration: none;
  color: black;
  font-size: 1.5rem;
}

.nav-item a:hover,
.nav-item a:active,
.nav-item a.nuxt-link-active {
  color: #f7797d;
}

.drawer-toggle {
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  height: 35px;
  width: 35px;
  cursor: pointer;
}
.bar {
  height: 2px;
  background: black;
  transition: all 0.3s ease-in-out 0.5s;
}
.bar:nth-child(1) {
  transform: rotate(135deg) translateY(-10px);
  transform-origin: bottom;
}
.bar:nth-child(2) {
  transform: rotate(-135deg) translateY(10px);
  transform-origin: top;
}</style>

TheSideNavToggle.vue

このコンポーネントは、ハンバーガーメニューの部分です。
TheHeader.vueにインポートして使います。

<template>
<!-- ハンバーガーメニュー -->
  <div
    class="drawer-toggle"
    role="button"
    @click="$emit('toggle')">
    <div class="bar"></div>
    <div class="bar"></div>
    <div class="bar"></div>
  </div>
</template>
<style scoped>
.drawer-toggle {
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  height: 50%;
  width: 35px;
  cursor: pointer;
}

@media (min-width: 768px) {
  .drawer-toggle {
    display: none;
  }
}

.drawer-toggle .bar {
  width: 90%;
  height: 2px;
  background-color: white;
}</style>

default.vue

次にレイアウトに組み込みます。
トグルするTheSideNavToggle.vueはTheHeader.vueにすでに組み込んであるので、
TheHeaderとTheSidenavをインポートします。

<template>
  <div>
    <TheHeader @sidenavToggle="displaySidenav = !displaySidenav" />
    <TheSidenav :show="displaySidenav" @close="displaySidenav = false" />

    <Nuxt />
  </div>
</template>

サイドバーを表示・非表示を切り替えたいので、displaySidenavという変数を用意して、true/falseで切り替えます。

<script>

import TheHeader from '@/components/Navigation/TheHeader'
import TheSidenav from '@/components/Navigation/TheSidenav'

export default {
  components: {
    // Header,
    TheHeader,
    TheSidenav,
  },
  data() {
    return {
      displaySidenav: false,
    }
  }
}</script>

あとは、pagesの各ページを作成すればOKです。

Vue.js

Posted by devsakaso