Vue.jsでjQueryの slideDown()とslideUp() 相当を行う方法

Vue.jsでjQueryの slideDown()とslideUp() 相当を行う方法

# 概要

jQueryの slideDown() / slideUp() の表示方法が気に入っていて、
Vue.jsでも出来ないかとやってみた、メモ書き。備忘録。

結論から言うと、Vue.jsの**標準機能の範囲で出来る**。
![スクリーンショット 2019-03-20 12.26.49.png](https://qiita-image-store.s3.amazonaws.com/0/125588/f2061ef0-06d9-e9a8-4d77-76b27007c84a.png)


具体的には、次のように構成することで、実装する。for文で変化量を計算して適用とか**不要**。

- `v-if` ディレクティブで対象エリアの表示／非表示を切り替える。
- `transition` タグ＋CSS3を用いて、表示／非表示の変化時を滑らかにする。

デモはこちら。⇒ [Vue.jsでのアコーディオンメニュー例](http://fluorite.halfmoon.jp/word/qiita201903_accordion/accordion_slide_menu.html)

なお、こういう「にょ～ん」と開いたり閉じたりするメニューを「アコーディオンメニュー」と呼ぶことがある、と今回に初めて知ったよ。「vue.js slideup() slidedown()」で検索してもイマイチ、これだ！ってのが見つからなくて、な。

なおなお、基本的には**CSSのtransition効果を用いた実現方法**の様子。なので、CSSを理解している人なら悩まない実装なのかもしれない（当方、CSSは分からんので、ね）。

# アコーディオンメニュー（slideDown/Up）の実装方法

次の順で、実装方法を説明する。
とりあえず動けばよい、って方は後半へ読み飛ばしOK。

1. `v-if` ディレクティブで対象エリアの表示／非表示の実装する。
2. `transition` タグを用いて、変化が瞬間ではなく連続的に起こるようCSSを適用する。


## メニューの表示／非表示切り替え

これは、Vue.jsの `v-if` を用いてそのまま実装するだけ。

[デモ](http://fluorite.halfmoon.jp/word/qiita201903_accordion/accordion_slide_menu0.html)　※これは瞬間的に表示／非表示が切り替わる版。未だ目的の前段階。

```html:accordion_slide_menu0.html
<!DOCTYPE html>
<html>
  <head>
  	<title>アコーディオンメニュー／SlideUp(), SlideDown()</title>
  
    <meta charset="UTF-8" />
    <!-- Font Awesome を利用 -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous"></link>

    <!-- CDN の vue.js を利用 -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>

    <!-- transitionにはCSS必須 -->
    <link rel="stylesheet" href="accordion_slide_menu.css"></link>
  </head>
<body>

    <div id="vue_slideupdown">
        <!-- vuejs-slidemenu -->
        <div class="cls_toggle_expand_collapse" v-on:click="toggleCtrlPanel">
            <i class="fa fa-caret-down"  v-if="isPanelShow"></i>
            <i class="fa fa-caret-right" v-if="!isPanelShow"></i>
            {{textToggle}}
        </div>
        <div class="menu_slide_accordion" v-if="isPanelShow">
            slideUp(), slideDown() したい内容を任意に書く。<br>
            純粋なhtmlでも良いし、<br>
            Vueの要素を書いても良い。<br>
        </div>
        <div>
            デモ。<br>
            <br>
            その他の要素。<br>
            この部分は、上部のメニューの開閉に伴ってアップダウンする。<br>
            ※こちらは「スライドアップ、スライドダウンの効果無し版」
        </div>
    </div>

    <!-- サンプルなので、簡便を目的にhtml内に記述 -->
    <script>
    var vueAccordion = new Vue({
        el:"#vue_slideupdown",
        data: {
            textToggle: "開閉するトグル（ここを押す）",
            isPanelShow: false
        },
        methods:{
            toggleCtrlPanel: function () {
                this.isPanelShow = !this.isPanelShow;
            }
        }
    })
    </script>
</body>
</html>
```

表示切替するエリアに適用するCSSは以下のようにしておく。なお、**CSSで表示サイズを指定**しておかないと、あとでSlideDown()/Up()相当を実装するときに上手く行かないので注意。※縦サイズのみが変化対象なので、縦サイズが指定されていれば良い。

```css:accordion_slide_menu.css
/*
 * [accordion_slide_menu.css]
 */

.cls_toggle_expand_collapse {
    margin  : 4px;
    padding : 8px;
    cursor  : pointer;
    background-color: aquamarine
}

/*
 * Slide / accordionメニューの設定
 */ 
.menu_slide_accordion {
    background-color: antiquewhite;
    height: 120px;
    overflow: hidden;
}
```


## 表示時の変化を連続的にする

上記の表示／非表示を切り替えに緩やかな時間変化を付ける。

[デモ](http://fluorite.halfmoon.jp/word/qiita201903_accordion/accordion_slide_menu.html)


これを行うには、先ず表示／非表示を切り替えるエリアである `<div class="menu_slide_accordion" v-if="isPanelShow">` の部分を、
`transition` タグで次のように囲む。


```
<transition name="trans_slide">
    <div class="menu_slide_accordion" v-if="isPanelShow">
        slideUp(), slideDown() したい内容を任意に書く。<br>
        純粋なhtmlでも良いし、<br>
        Vueの要素を書いても良い。<br>
    </div>
</transition>
```

すると、「囲んだ要素内のDOMが追加、更新、削除したときにトランジション効果を適用」することが**容易に**できる。具体的には次のように「`transition`タグのname属性名＋`-xxxx`」というクラス指定を用いて「変化の仕方」と「変化させるパラメータ」を指定する。

```
/* Vueの方で自動的に付与されるクラス::-enter-xx */
.trans_slide-enter-active, .trans_slide-leave-active {
    transition: opacity .5s;
}
.trans_slide-enter, .trans_slide-leave-to {
    opacity: 0;
}
```

上記のCSS表記例は、次ような「変化」を指定する内容。動作としては「挿入時に、1秒をかけて透明から不透明に変化する」＋「削除人、1秒をかけて不透明から透明に変化する」というアニメーションになる。

- DOMが挿入**されようと**したら（`xxx-enter`）、「`opacity = 0`」に設定する。
- DOM挿入後の状態に向かって、`opacity`属性を、`5s`の時間をかけて変化させる。
    - ここで「挿入後」とは「表示が完了した」状態なので、「`opacity = 1`」を意味する。
    - この「〇〇属性を、xxの時間をかけて変化させる」はCSS3の `transition` 機能そのもの。この例であれば「変化中(`xxx-enter-active`)は `transition: opacity .5s` に従う」という設定の仕方となる。
- DOMが削除**された**ら(`xxx-leave-to`)、「`opacity = 0`」に設定する。
- DOM削除後の状態に向かって、`opacity`属性を、`5s`の時間をかけて変化させる。
    - ここで「削除後」の状態として「`opacity = 0`」の状態を指定しているので、この変化の開始前の「`opacity = 1`」に対して「1→0」と変化させることを意味する。
    - 削除へ向かう変化中（`xxx-leave-active`）の変化の仕方は、同様にCSS3の `transition` 機能で指定する。

透明度を変化させる代わりに、次のように`height`属性値の変化を指定することで「にょ～ん、と上から下へ開く。しゅるる～、と下から上へ閉じる」という**slideDown(), slideUp() 相当のアニメーション動作を実装できる**。

```
/* Vueの方で自動的に付与されるクラス::-enter-xx */
.trans_slide-enter-active, .trans_slide-leave-active {
    transition: all .5s;
}
.trans_slide-enter, .trans_slide-leave-to {
    height: 0;
}
```

slideDown(), slideUp() 相当の動作を設定したCSSファイルの全体像は、次のようになる。

ここで、変化対象とする`div`クラスの`width`属性の初期値（挿入時＝表示時の初期値）を**明記する**ことを忘れずに。さもないと変化の向かう先が見つからず、上手くアニメーションにならない。それから、「`overflow: fidden;`」を指定しておくことで「高さが小さくなったdiv要素からはみ出したテキストを**表示しない**」設定にすることを忘れずに。これを設定しないと、divの動作から置いて行かれてはみ出したテキストが残り、見た目がイマイチになる。


```css:accordion_slide_menu.css
/*
 * [accordion_slide_menu.css]
 */

.cls_toggle_expand_collapse {
    margin  : 4px;
    padding : 8px;
    cursor  : pointer;
    background-color: aquamarine
}

/*
 * Slide / accordionメニューの設定
 */ 
.menu_slide_accordion {
    background-color: antiquewhite;
    height: 120px;
    overflow: hidden;
}
/* Vueの方で自動的に付与されるクラス::-enter-xx */
.trans_slide-enter-active, .trans_slide-leave-active {
    transition: all .5s;
}
.trans_slide-enter, .trans_slide-leave-to {
    height: 0;
}
```

`transition` タグを追加した、htmlコード全体は次のようになる。メニューを開閉するjavascriptコード側の変更は**不要**。これで、アコーディオンメニュー／SlideUp(), SlideDown()相当の動作実装は完了ー。

[デモ](http://fluorite.halfmoon.jp/word/qiita201903_accordion/accordion_slide_menu.html)　※本節の冒頭のリンク先と同一。


```html:accordion_slide_menu.html
<!DOCTYPE html>
<html>
  <head>
  	<title>アコーディオンメニュー／SlideUp(), SlideDown()</title>
  
    <meta charset="UTF-8" />
    <!-- Font Awesome を利用 -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous"></link>

    <!-- CDN の vue.js を利用 -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>

    <!-- transitionにはCSS必須 -->
    <link rel="stylesheet" href="accordion_slide_menu.css"></link>
  </head>
<body>

    <div id="vue_slideupdown">
        <!-- vuejs-slidemenu -->
        <div class="cls_toggle_expand_collapse" v-on:click="toggleCtrlPanel">
            <i class="fa fa-caret-down"  v-if="isPanelShow"></i>
            <i class="fa fa-caret-right" v-if="!isPanelShow"></i>
            {{textToggle}}
        </div>
        <transition name="trans_slide">
            <div class="menu_slide_accordion" v-if="isPanelShow">
                slideUp(), slideDown() したい内容を任意に書く。<br>
                純粋なhtmlでも良いし、<br>
                Vueの要素を書いても良い。<br>
            </div>
        </transition>
        <div>
            デモ。<br>
            <br>
            その他の要素。<br>
            この部分は、上部のメニューの開閉に伴ってアップダウンする。<br>
        </div>
    </div>

    <!-- サンプルなので、簡便を目的にhtml内に記述 -->
    <script>
    var vueAccordion = new Vue({
        el:"#vue_slideupdown",
        data: {
            textToggle: "開閉するトグル（ここを押す）",
            isPanelShow: false
        },
        methods:{
            toggleCtrlPanel: function () {
                this.isPanelShow = !this.isPanelShow;
            }
        }
    })
    </script>
</body>
</html>
```

以上ー。

# 蛇足

感想など。

CSS3のtransition属性を知っていれば、悩まない話だったポイ。CSSは全く分からんからなぁ、私は。この実装方法に辿り着くまで大分悩んだよ。。。


# 参考サイト

- Enter_Leave とトランジション一覧 — Vue.js (公式サイト)
    - https://jp.vuejs.org/v2/guide/transitions.html#CSS-%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B8%E3%82%B7%E3%83%A7%E3%83%B3
- Vue.js-transition slideUp_slideDown
    - https://codepen.io/kdydesign/pen/VrQZqx
- Vue.jsで、スライドメニューを作る
    - https://poridiet.com/vuejs-slidemenu/
- 【CSS3】Transition（変化）関連のまとめ - Qiita
    - https://qiita.com/7968/items/812d6a21fc4dd9ae9c75
- Vue.jsのトランジションとCSSで作るアニメーションの基本をサンプルでわかりやすく解説 – Nyle Engineering Blog
    - https://medium.com/nyle-engineering-blog/vue-js%E3%81%AE%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B8%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%A8css%E3%81%A7%E4%BD%9C%E3%82%8B%E3%82%A2%E3%83%8B%E3%83%A1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E5%9F%BA%E6%9C%AC%E3%82%92%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%81%A7%E3%82%8F%E3%81%8B%E3%82%8A%E3%82%84%E3%81%99%E3%81%8F%E8%A7%A3%E8%AA%AC-d594a263497d