LoginSignup
6
2

More than 3 years have passed since last update.

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

Posted at

はじめに

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

アイツ:point_down:
btmnav4.png

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

出来上がるもの

003.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

フローティングボタンを用意する

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

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

完成、そんなことはなかった

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

002.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

...というわけで、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

仕上げ

フローティングボタンをボトムナビゲーションに重ねます。
まず、フローティングボタンを配置している 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

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

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

まとめ

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

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

6
2
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
6
2