Nuxt3でアコーディオンメニューの動きを作る
Nuxt3でアコーディオンメニューの動きを作る
こんにちは、合同会社Stegの@keigoです。 今回は、Nuxt3 (Vue3) でアコーディオンメニューの動きを実装したときの手法を書いておきます。 もっといい実装の仕方があったらおしえてください。
完成品は以下のようなものです。 Vuetifyでいう、ExpansionPanelsです。
まずはアニメーションなしで作る
template部分 (styleはまわりは省略)
<template>
<div ref="expansionPanel" class="expansion-panel">
<div @click="toggleExpansionPanel()" class="expansion-panel-title">
<span>
タイトル
</span>
<span class="arrow">
</div>
<div ref="hiddenExpansionPanel" class="expansion-panel-hidden-container">
<span>コンテンツコンテンツ</span>
<span>コンテンツコンテンツ</span>
</div>
</div>
</template>
上記コードの3行目に、@click="toggleExpansionPanel()"
があるかと思います。こちらのクリックイベントにて、閉じ開きをトリガーします。
開く前(閉じられた状態)は、以下のような状態になっています。
クリックして開くと、以下のような状態になります。
しかし、heightが固定値pxではないと、transition: all 0.2s ease-out;
のようなトランジションは効きません。
したがって、要素の高さのpxを取得し、固定値で指定してあげる必要があります。
閉じ開きのアニメーションを作る
要素の高さをVue3記法で取得するには、以下のようなコードになります。
<script setup>
const expansionPanelOpenedHeight = ref(0 + 'px') // cssに入れる用の変数宣言
const expansionPanel = ref(null)
const hiddenExpansionPanel = ref(null)
onMounted(() => {
const topContainerHeight = expansionPanel.value.offsetHeight // 開く前の要素の高さ
const hiddeinContainerHeight = hiddenExpansionPanel.value.offsetHeight // 隠れている部分の要素の高さ
expansionPanelOpenedHeight.value = topContainerHeight + hiddeinContainerHeight + 'px' // 上二つの合計を固定値pxの文字列にして代入
})
</script>
上記コードによって、expansionPanelOpenedHeight
という変数にXXXpx
という値が格納されました。
あとはcssにて、
.open {
height: v-bind(expansionPanelOpenedHeight);
}
と書いてあげることによって、変数が使用できます。
clickイベントなどによって上記 .open
クラスを付与し、高さを指定してあげることができます。
クリックして開くと、以下のような状態になります。
おまけ
ホバー時のオーバーレイを作る
これ地味に面倒なんですよね、やった人にはわかると思うのですが、hoverでbackground-colorを普通に変えると、padding部分が効かなくなってしまうんですよね。
↓こんなかんじになっちゃう
自分は、親要素をposition: relative;
、擬似要素でposition: absolute
(例の要素を重ねるやつ) したうえで、擬似要素のwidthとheight指定によってpadding部分もちゃんと塗ってくれるようにしました。
あとは、
&:hover {
&::after {
background-color: $black;
opacity: 0.02;
}
}
でホバーしたら擬似要素の色と透明度を指定するようにしました。
矢印が回転するようにする
これは別のコンポーネントに切り出しました。
expansionPanelToggle
というbooleanのpropsを渡してあげて、あとは子コンポーネントでclassを付与したりしなかったりしています。
<svg :class="{ rotate: expansionPanelToggle }"
svg {
transition: all 0.2s ease-out;
}
.rotate {
transform: rotate(180deg);
}
ちなみにVue3では以下のようにPropsを受け取ります
<script setup lang="ts">
interface Props {
expansionPanelToggle: boolean
}
const props = withDefaults(defineProps<Props> (), {
expansionPanelToggle: false
});
</script>