のぐそんブログ

暗いおじさんがシコシコ書くブログです。

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が非表示に関するクラスです。

f:id:nogson2:20181229164029p:plain Vue.js公式より

トランジションクラスの命名規則

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(非表示)が同時に発生します。 その場合、同じ位置に表示したいのですが、切り替えのタイミングでボタンの位置がガタつきます。

f:id:nogson2:20181229164033g:plain

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