Vuetifyでボトムナビゲーション上にフローティングボタンをつける

# はじめに
Vuetify を使うと優れた UI をとても簡単に作れますが、少し特殊なことをやろうとするとつまづくことが多いです。
本記事では、モバイルアプリでよく見かける、**ボトムナビゲーションからはみ出たアイツ** をつける方法を記載します。

アイツ:point_down:
![btmnav4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150372/2dc7763b-6025-9b36-697a-daf080a6425e.png)

アイツの一般的な名称は、多分「オーバーラップ FAB」なんですが。

## 出来上がるもの
![003.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150372/922838a6-3591-3ef4-8628-846ded47993c.gif)

## 開発環境
- Windows10
- Node.js 12.13.1
- Atom 1.42.0
- vue/cli 4.1.1
- Vue 2.6.1
- Vuetify 2.0.3

## アイコンをインストールする
とりあえずアイコンを用意しましょう。今回は、Fontawesome を使います。
環境に合わせてご自由にどうぞ。

```
npm install @fortawesome/fontawesome-free -D
```

```:main.js
import Vue from 'vue'
import App from './App.vue'
import vuetify from './plugins/vuetify';
import '@fortawesome/fontawesome-free/css/all.css' /* この行を追加する */
```

## ボトムナビゲーションを追加する
v-app ディレクティブの後ろにコードを追加します。

```:App.vue
<template>
  <v-app>
    
    <!-- 省略 -->
    
    <!-- ここからボトムナビゲーション -->

    <v-bottom-navigation
      dark
      grow
    >
      <v-btn>
        <span>Home</span>
        <v-icon>fas fa-home</v-icon>
      </v-btn>
      <v-btn>
        <span>Edit</span>
        <v-icon>fas fa-edit</v-icon>
      </v-btn>
    </v-bottom-navigation>
    
    <!-- ここまでボトムナビゲーション -->

  </v-app>
</template>
```

ボトムナビゲーションが表示されました！
![btmnav1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150372/0923f846-8b95-b550-2c40-0518b6558312.png)

## フローティングボタンを用意する
Vuetify では v-speed-dial ディレクティブを使うと、子ボタンを内包したフローティングボタンが使えます。

```:App.vue
<template>
  <v-app>
    
    <!-- 省略 -->

    <!-- ここからフローティングボタン -->

    <v-layout justify-center row wrap>
      <v-speed-dial
        v-model="fab"
        >
        <template v-slot:activator>
          <v-btn
            color="red darken-2"
            dark
            fab
          >
            <v-icon v-if="fab">mdi-close</v-icon>
            <v-icon v-else>mdi-plus</v-icon>
          </v-btn>
        </template>
        <v-btn
          fab
          dark
          small
          color="orange"
        >
          <v-icon>mdi-note</v-icon>
        </v-btn>
        <v-btn
          fab
          dark
          small
          color="red"
        >
          <v-icon>mdi-camera</v-icon>
        </v-btn>
        <v-btn
          fab
          dark
          small
          color="indigo"
        >
          <v-icon>mdi-file</v-icon>
        </v-btn>
      </v-speed-dial>
    </v-layout>

    <!-- ここまでフローティングボタン -->

    <!-- 省略 -->

<script>

export default {
  name: 'App',

  components: {
  },

  data: () => ({
    fab: false, /* フローティングボタン のために追加 */
  }),
};
</script>

```

![001.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150372/e186ba9d-e5e2-0002-3a0f-82ef02860748.gif)

あとはフローティングボタンを、ボトムナビゲーションに入れたら完成ですね:star:

## 完成、そんなことはなかった
すみません、どうやら簡単にはできないようです。
ボトムナビゲーションにフローティングボタンを入れると、とても残念な結果になります。

![002.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150372/81e3b2fa-9e46-e457-2e60-52c2757f590c.gif)


一応ソースも置いておきます。  

```:App.vue
<template>
  <v-app>
    
    <!-- 省略 -->

  <v-bottom-navigation
      dark
      grow
    >
      <v-btn>
        <span>Home</span>
        <v-icon>fas fa-home</v-icon>
      </v-btn>
 
      <!-- ここからフローティングボタン -->

      <v-layout justify-center>
        <v-speed-dial
          v-model="fab"
          >
          <template v-slot:activator>
            <v-btn
              color="red darken-2"
              dark
              fab
            >
              <v-icon v-if="fab">mdi-close</v-icon>
              <v-icon v-else>mdi-plus</v-icon>
            </v-btn>
          </template>
          <v-btn
            fab
            dark
            small
            color="orange"
          >
            <v-icon>mdi-note</v-icon>
          </v-btn>
          <v-btn
            fab
            dark
            small
            color="red"
          >
            <v-icon>mdi-camera</v-icon>
          </v-btn>
          <v-btn
            fab
            dark
            small
            color="indigo"
          >
            <v-icon>mdi-file</v-icon>
          </v-btn>
        </v-speed-dial>
      </v-layout>

      <!-- ここまでフローティングボタン -->

      <v-btn>
        <span>Edit</span>
        <v-icon>fas fa-edit</v-icon>
      </v-btn>
    </v-bottom-navigation>
  </v-app>
</template>
```

ボタンのスタイルを !important で無理やり書き換えれば多少の抵抗はできそうですが、時間のムダです。
もちろん僕はやりました:innocent:
やめておいた方が吉です:middle_finger:

## ボトムナビゲーションとフローティングボタンは分離する
ボトムナビゲーション内にフローティングボタンは置けません:skull:

ボトムナビゲーションは画面の下部に固定されるので、ついでにフローティングボタンもボトムナビゲーションの中に含めたくなりますが、それではうまくいきません。
そこで、v-footer ディレクティブを用意し、その中に、フローティングボタンとボトムナビゲーションを入れます。

![newdir.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150372/bab99d41-c269-bc74-9a6a-cdbbf3c0a06b.jpeg)

...というわけで、v-footer ディレクティブの中に、フローティングボタンとボトムナビゲーションを入れます。
また、v-footer には fixed プロパティを付与し、画面下部に固定します。

```:App.vue
<template>
  <v-app>
    
    <!-- 省略 -->

  <v-footer
     padless
     fixed
      >
      <v-row>
        <v-col
          cols="12"
          class="pa-0"
          >
          <v-layout justify-center>
            <v-speed-dial
              v-model="fab"
              >
              <template v-slot:activator>
                <v-btn
                  color="red darken-2"
                  dark
                  fab
                  class="float"
                >
                  <v-icon v-if="fab">mdi-close</v-icon>
                  <v-icon v-else>mdi-plus</v-icon>
                </v-btn>
              </template>
              <v-btn
                fab
                dark
                small
                color="orange"
              >
                <v-icon>mdi-note</v-icon>
              </v-btn>
              <v-btn
                fab
                dark
                small
                color="red"
              >
                <v-icon>mdi-camera</v-icon>
              </v-btn>
              <v-btn
                fab
                dark
                small
                color="indigo"
              >
                <v-icon>mdi-file</v-icon>
              </v-btn>
            </v-speed-dial>
          </v-layout>
        </v-col>
        <v-col
         cols="12"
         class="pa-0"
          >
          <v-bottom-navigation
            dark
            grow
          >
            <v-btn>
              <span>Home</span>
              <v-icon>fas fa-home</v-icon>
            </v-btn>
            <v-btn>
              <span>Edit</span>
              <v-icon>fas fa-edit</v-icon>
            </v-btn>
          </v-bottom-navigation>
        </v-col>
      </v-row>
    </v-footer>
  </v-app>
</template>
```

ようやくまともに表示できました:relieved:
![btmnav2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150372/59f0d9a2-c107-3d0b-cc1c-39f74c49b604.png)

## 仕上げ
フローティングボタンをボトムナビゲーションに重ねます。
まず、フローティングボタンを配置している row の高さを 0 にします。

```:App.vue
<template>
  <v-app>
    
    <!-- 省略 -->

    <v-footer
     padless
     fixed
      >
      <v-row>
        <v-col
          cols="12"
          class="pa-0"
          style="height: 0px;"  <!-- 高さを 0 にする -->
          >
          <!-- 省略 -->
        </v-col>
      </v-row>
    </v-footer>
  </v-app>
</template>
```

完全に重なりましたね。
![btmnav3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150372/3d6a6047-7325-62eb-fff9-e451220ac2bb.png)


次に、フローティングボタンを上方向にズラすために、スタイルを適用します。

```:App.vue
<template>
  <v-app>
    
    <!-- 省略 -->

              <template v-slot:activator>
                <v-btn
                  color="red darken-2"
                  dark
                  fab
                  class="float"  <!-- 位置調整用のスタイルを適用 -->
                >
                  <v-icon v-if="fab">mdi-close</v-icon>
                  <v-icon v-else>mdi-plus</v-icon>
                </v-btn>
              </template>
              
    <!-- 省略 --> 
 
  </v-app>
</template>

<script>
export default {
  name: 'App',
  components: {
  },
  data: () => ({
    fab: false,
  }),
};
</script>

<style>
.float {
  -webkit-transform: translateY(-20%); /* フローティングボタンを上部にズラす */
  transform: translateY(-20%);
}
</style>

```

ようやく狙った位置に置くことができました:smile:
![btmnav4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150372/4e148bd1-d3d9-f75e-8b37-45e0e26a959d.png)

## まとめ
Vuetify は色々と捗り過ぎて素晴らしいですが、単純にやり過ぎるとドツボにハマります。
やはりどのようなフレームワークを使おうが CSS の基本的な知識（特にFlexbox）は大事ですね。

あと、もっと楽な方法あったらコメントいただけると嬉しいです！