vueでtransitionを利用するメモ
vueではtransitionコンポーネントを利用することで、わりと簡単にアニメーションを実現することができます。
公式を見たほうがより詳しいので、公式を見ることをおすすめします。
基本
ポイントは、以下3点です。 * transitionコンポーネントで対象を囲む * アニメーションの対象にv-if、v-showなどを設定 * cssにアニメーションクラスを定義
<template> <div> <div class="container"> <transition> <span class="box" v-if="show" ></span> </transition> </div> <button @click="show = !show">fade</button> </div> </template> <style> .container{ height: 100px; } .box { display: block; width: 50px; height: 50px; background: #000; } button{ display: block; } /* アニメーション処理 */ .v-leave-active, .v-enter-active { transition: opacity 1s; } .v-enter, .v-leave-to { opacity: 0; } </style> <script> export default { data: function() { return { show: true }; } }; </script>
See the Pen Vue.js Simple Transition 01 by nogson (@satofaction) on CodePen.
トランジションクラス
enterが表示に関するクラスです。 leaveが非表示に関するクラスです。
トランジションクラスの命名規則
transitionコンポーネントに名前(name)がない場合、デフォルトでv-
が付きます。 name属性を指定した場合はvの代わりにプレフィックスにnameで指定した文字列が設定されます。
例)名前なし
.v-enter
例)名前 = ‘fade’
.fade-enter
初期描画時のトランジション
appearをつけることで表示のタイミングでアニメーションを実行することができる。
<transition appear> <span class="box" v-if="show"></span> </transition>
要素間のトランジション
v-if、v-elseを利用した要素間のトランジションの設定できます。 ポイントは、同じタグ名の要素でトグルする場合は、key属性で個別の名前を設定する必要があります。
例)同じタグ名の場合はkey属性が必要
<transition> <span v-if="show" class="box" key="box"></span> <span v-else class="box2" key="box2"></span> </transition>
例)タグ名が別の場合はkey属性は不要
<transition> <span v-if="show" class="box"></span> <i v-else class="box2"></i> </transition>
See the Pen Vue.js by nogson (@satofaction) on CodePen.
javascriptのフックイベント
transitionコンポーネントには、アニメーションの各過程でフックできるイベントがあります。
イベントの種類
イベント | タイミング |
---|---|
before-enter | 要素が表示される前 |
enter | 要素が表示されてアニメーションされる前 |
after-enter | 表示アニメーション後 |
enter-cancelled | 表示キャンセル時 |
before-leave | 非表示アニメーションが実行される前 |
leave | 非表示アニメーションが実行される前でbefore-leaveの後 |
after-leave | 要素が非表示された後 |
leave-cancelled | 非表示がキャンセル時 |
イベントの設定方法
各イベントはv-on
で設定します。
<transition @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter"> <span v-if="show" class="box"></span> </transition>
関数はmethodsに定義します。 以下の感じです。
第一引数にはアニメーションするエレメントが渡ってきます。 enter、leaveの第二引数にはコールバックオプションが渡ってきます。
methods: { beforeEnter: function(el) { console.log("beforeEnter", el); }, enter: function(el,done) { console.log("enter", el,done); }, afterEnter: function(el) { console.log("afterEnter", el); } }
コールバックオプションはトランジションの終了を明示的に制御する為に利用するみたいなのですが、イマイチ動きがわかりませんでした。。。
jsだけでアニメーションする場合は、かならずコールバックオプションを実行する必要があるみたいです。
cssアニメーションを無効にする
jsのみで制御する場合に、cssのアニメーションが干渉するのを防ぎます。
<transition @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter" :css="false" >
リストトランジション
v-forなどで複数の要素をアニメーションさせる場合は、transition-group
を利用します。
<template> <div> <div class="container"> <transition-group tag="ul"> <li v-for="item in items" v-bind:key="item.key" class="box">{{ item.val }}</li> </transition-group> </div> <button v-on:click="add">Add</button> <button v-on:click="remove">Remove</button> </div> </template> <style> .container { height: 100px; } .box { display: inline-block; width: 50px; height: 50px; background: #000; margin-right: 10px; } button { display: block; } .v-enter-active, .v-leave-active { transition: all 1s; } .v-enter, .v-leave-to /* .v-leave-active for below version 2.1.8 */ { opacity: 0; transform: translateY(30px); } .v-move { transition: transform 1s; } </style> <script> export default { data: function() { return { show: false, items: [], }; }, methods: { add: function() { //this.items.splice(this.randomIndex(), 0, this.nextNum++); const item = { val: "", key: new Date().getTime() }; this.items.push(item); }, remove: function() { this.items.pop(); } }, mounted() {} }; </script>
いくつかポイントがあります。
tag
tag
にはtransition-groupを置き換えるタグを指定します。 指定しない場合は、span
が適応されます。
中の要素にkeyは必須
v-forする要素にはkey属性が必須です。 keyに設定する要素は、index以外のものがよいです。indexは要素の追加、削除で入れ替わる可能性があり、アニメーションする際にうまく動かない場合があります。
中の要素にはdisplay:inlineを指定しない
中の要素にdisplay:inline
を指定するとうまくアニメーションしない場合があります。inline-block
などにしたほうが良さそうです。
-moveクラス
表示、非表示だけでなく位置の変化もアニメーションさせることができます。 要素の位置が変化する際に-move
クラス追加されます。
トランジションモード
下のボタンのトグル表示のような場合、enter(表示)とleave(非表示)が同時に発生します。 その場合、同じ位置に表示したいのですが、切り替えのタイミングでボタンの位置がガタつきます。
<template> <div> <transition> <button v-if="show === true" @click="show = !show" key="button1">ON</button> <button else @click="show = !show" key="button2">OFF</button> </transition> </div> </template> <style> .container { height: 100px; } .v-leave-active, .v-enter-active { transition: opacity 1s; } .v-enter, .v-leave-to { opacity: 0; } </style> <script> export default { data: function() { return { show: true, }; } }; </script>
このようなケースの場合にトランジションモードを利用します。
in-out
新しい要素の表示が完了した後に、現在の要素の非表示が開始する
out-in
現在の要素が非表示になってから、新しい要素の表示が開始する
<transition mode="in-out"> </transition>
今回のケースでは非表示(leave)が終わってから、表示(enter)が始まるようにします。
<template> <div> <transition mode="out-in"> <button v-if="show === true" @click="show = !show" key="button1">ON</button> <button else @click="show = !show" key="button2">OFF</button> </transition> </div> </template>