Last Updated: 3/26/2020, 3:07:04 PM

# Vue.js の transition ってなに?

transition タグは主に「追加した時に、ふわっとフェードインさせたり」、 あるいは「削除した時に、ふわっとフェードアウトさせたり」する時に使うもの」のようです。

Vue は、DOM からアイテムが追加、更新、削除されたときにトランジション効果を適用するための方法を複数提供しています:
Enter/Leave とトランジション一覧 - Vue.js

Hello, world!

# 1. 勘違いしていたこと

transition タグは、  アニメーション全般に使えるタグではありません。  transition タグは、DOM の追加と削除の描画に特化したタグです。

白状すると  CSS の transition プロパティ  と、  Vue.js の transition タグ  を 区別できていませんでした。いま振り返ってみると致命的です笑

自分は最初、transition はタグとして専用に用意されているのだから、 相当すごいことをするのだろうと思いました。 そして「transition はアニメーション全般に対して使うもの」と勘違いして、沼にはまっていました。

結果、あさっての方向に向かって3万光年くらい進んでしまいました。 長文の質問を teratail にしたり、 まったく意味のない箇所で transition タグで囲んで 「transition 完全に理解した」と思って悦に浸っていました。 もはやキチガイです。

# 2. 使い方

以下の CodePen を元に話を進めます。

See the Pen Vue.js transition tag by niconico25 (@niconico25) on CodePen.

# Step 1. CSS を書く

はじめを v-enter, transition している間を v-enter-active , おわりを v-enter-to で記述してあげます。

コードは以下の様な具合です。

/* はじめ */
.v-enter {
  /* 最初は表示して欲しくないので 0 */
  opacity: 0;
}
/* transition している間 */
.v-enter-active {
  transition: opacity 1s
}
/* おわり */
.v-enter-to {
  /* 終わったら表示して欲しいので 1 */
  opacity: 1;
}

transition はちゃんとプロパティを指定してあげたほうがベターだそうです。 勉強になります。ありがとうございます。

# Step 2. HTML を書く

transition タグで enter と leave のアニメーションを付与したい DOM を  1つだけ  囲みます。  1つだけ  です。大事なことなので、2回言いました的な...

<div id="app">
  <button @click="show=!show">
    click
  </button>
  <!-- transition タグで囲みます。 -->
  <transition> 
    <!-- 配下の DOM は1つだけ -->
    <div v-if="show"> 
      Hello, world!
    </div>
  </transition>
</div>

# Step 3. Vue.js を書く

new Vue({
  el: '#app',
  data: {
    show: true,
  }
})

# 3. CSS クラスの詳細

「開始状態を v-enter, transition している期間中を v-enter-active , 終了状態を v-enter-to で記述してあげます。」と前節で書きました。 が、実際の CSS の適用のされ方は以下のような具合になります。

# ◯ .v-enter クラス

1. v-enter:
enter の開始状態。 要素が挿入される前に適用され、要素が挿入された 1 フレーム後に削除されます。

# ◯ .v-enter-active クラス

2. v-enter-active:
enter の活性状態。トランジションに入るフェーズ中に適用されます。 要素が挿入される前に追加され、トランジション/アニメーションが終了すると削除されます。 このクラスは、トランジションの開始に対して、期間、遅延、およびイージングカーブを定義するために使用できます。

# ◯ .v-enter-to クラス

3. v-enter-to:
バージョン 2.1.8 以降でのみ利用可能です。enter の終了状態です。 要素が挿入された1フレーム後に追加されます(同時に v-enter が削除されます) 、トランジション/アニメーションが終了すると削除されます。

ちょっとだけ訳がおかしかったので、こちらで修正したものを記述しています。

# 4. 補足

# ◯ .v-enter-to の省略

v-enter-to には opacity: 1 を設定していました。 これは規定値と同じです。 そのため、今回の例では v-enter-to を省略することができます。

.v-enter {
  opacity: 0;
}
.v-enter-active {
  transition: opacity 1s
}
/*
.v-enter-to {
  opacity: 1;
} 
*/

# ◯ 表示できる要素は直下の1つだけ

transition は「単一 要素/コンポーネントのトランジション」です。 そのため、transition タグ直下に同時に3つの要素を表示しようとしても、できません。

例えば、以下のコードを見てください。

<div id="app">
  <transition>
    <div v-if="show">Hello, world!</div> <!-- 表示される -->
    <div v-if="show">你好,世界!</div> <!-- 表示されない -->
    <div v-if="show">こんにちは、世界!</div> <!-- 表示されない -->
  </transition>
  <button @click="show=!show">
    click
  </button>
</div>

表示される要素は、一番上の Hello, world! だけであることが確認できます。

See the Pen Vue.js transition tag - 2 by niconico25 (@niconico25) on CodePen.

# ◯ 要素間トランジッション

繰り返しになりますが、  通常は transition タグ配下には、要素は1つだけしか書くことができません。 

しかし v-if を使って transition タグ配下に1つだけ、 要素が表示されるように書いた時は、 2つ以上の要素が transition タグ配下にあっても、 期待したような動作をしてくれます。

See the Pen Vue.js transition tag - 3 by niconico25 (@niconico25) on CodePen.

<!-- 消えて out から表示 in する -->
<transition mode="out-in">
  <!-- key を指定しないと動かない -->
  <div v-if="     show==='0'" key="0">
    Hello, world!
  </div>
  <div v-else-if="show==='1'" key="1">
    你好,世界!
  </div>
  <div v-else-if="show==='2'" key="2">
    こんにちは、世界!
  </div>
</transition>

v-for, v-if と組み合わせればスライドショーのようなものが作れます。

See the Pen Vue.js Slide Show by niconico25 (@niconico25) on CodePen.

この機能を特に「要素間トランジッション」と呼ぶらしいです。 詳細は公式サイトをご確認ください。

# ◯ 名前をつけてあげたい

transition を1つの要素だけではなく、2つ以上の要素につけたい時があります。 そんなときには名前をつけて区別させてあげます。

<!-- 名前なし -->
<transition>
  <img src="./fig.jpg">
</transition>

<!-- 名前つき -->
<transition name="my-transition">
  <img src="./fig.jpg">
</transition>
/* 名前なし */
/* 頭に v- をつける */
.v-enter {
  opacity: 0;
}
.v-enter-active {
  transition: opacity 1s
}
.v-enter-to {
  opacity: 1;
}

/* 名前つき */
/* 頭に 名前- をつける  */
/* 名前-enter  */
.my-transition-enter {
  opacity: 0;
}
/* 名前-enter-active */
.my-transition-enter-active {
  transition: opacity 1s
}
/* 名前-enter-to */
.my-transition-enter-to {
  opacity: 1;
}

各クラスは、トランジションの名前が先頭に付きます。 <transition> 要素に名前がない場合は、デフォルトで v- が先頭に付きます。例えば、 <transition name="my-transition"> の場合は、 v-enter クラスではなく、my-transition-enter となります。
トランジションクラス - Vue.js

# 5. 属性 - appear と v-cloak

appear, v-cloak について見ていきます。 インスタンスを作成 ボタンを押すと Vue インスタンスが生成されます。

See the Pen Vue.js appear and v-cloak by niconico25 (@niconico25) on CodePen.

<!-- 1. -->
<transition>
  <svg v-if="show" height="100" width="100">
    <circle cx="50" cy="50" r="50" fill="red" />
  </svg>
</transition>

<!-- 2. appear -->
<transition appear>
  <svg v-if="show" height="100" width="100">
    <circle cx="50" cy="50" r="50" fill="yellow" />
  </svg>
</transition>

<!-- 3. v-cloak -->
<transition v-cloak>
  <svg v-if="show" height="100" width="100">
    <circle cx="50" cy="50" r="50" fill="green" />
  </svg>
</transition>

<!-- 4. appear v-cloak -->
<transition appear v-cloak>
  <svg v-if="show" height="100" width="100">
    <circle cx="50" cy="50" r="50" fill="blue" />
  </svg>
</transition>

Vue インスタンスを破棄しないと、動作を再現できないのですが、 する方法がわかりませんでした。 右下にある Rerun ボタンをクリックして、再起動してください。

# ◯ appear 属性

appear 属性を書くと  ページを開いたときに  、ふわっと表示する動作を実装できます。 appear 属性を指定された transition タグは、ページを開いた時に、要素が追加されます。

ノードの初期描画時にトランジションを適用したい場合は、appear 属性を追加することができます:
Enter/Leave とトランジション一覧 - Vue.js

# ◯ v-cloak 属性

v-cloak 属性を使うと  ページを開いたときに  、 変な表示のされ方を避けることができます。 変な表示とは、Vue.js がコンパイルする前の要素です。 v-cloak は Vue インスタンスのが完了すると自動的に外される属性です。

v-cloak - API
このディレクティブは関連付けられた Vue インスタンスのコンパイルが終了するまでの間残存します。

v-cloak 属性で、はまった方の記事を見かけたので引用いたします。

v-cloak ディレクティブを使うときは、 インラインで display:none; のスタイルを書いちゃだめ。 v-cloakが効かない - Qiita

しかし、v-ifと組み合わせるとif判定のほうが優先され、 条件が真になった時点で要素を表示してしまう なので、v-cloakとv-ifの組み合わせはNG v-cloakが効かない件 - ISSUS

# 6. 背景 - transition タグの意味

Vue.js は主に DOM の操作、HTML に関わる機能を提供してくれていますが、 Vue.js の transition タグは DOM の装飾、CSS に関わる機能を提供してくれています。 transition タグだけちょっと仲間外れです。 なぜ transition タグが、存在するのでしょうか?

まず CSS のクラスの切り替えだけでも同じことができるらしいですが、 それだと色々と辛いらしいです。

CSS transitionで頑張らないVue.js transition - Qiita

CSSとクラス切り替えだけで行うトランジションのつらさ
これで実際に表示されるかと思いますが、以下のような問題があるかと思います。

  • 常にDOM上に要素自体は表示されるため、v-ifなどでシンプルに記述できず、記述がわかりづらくなる
  • コンポーネントなどの表示を切り分ける場合、 created() が表示前に実行されるため、 「開いた時にcreatedしたい」みたいなことができない

また jQuery を使っても辛いらしいです。

jQuery から Vue.js へのステップアップ - Qiita
display:none が付いてるので、切り替えのフェードインにはトランジションの代用でアニメーションを使用しました。 visibility などを使ってもいいけど、このへんの実装も CSS 大好きじゃないとつらくなってきます。

transition は、いわば糖衣構文ということでしょうか。 こういう文章はありがたいです...

# 7. おわりに

ここまで以下のように見てきました。

色々はまって大変でした笑 以上になります。ありがとうございました。