Vue.js の transition-group で
リストのアニメーションに挑戦

CSS, JavaScript, SCSS, Vue.js

以前、Jazz の歴史本を題材に Vue.js と YouTube API をつかったプレイリストページを作りました。

ページでは、曲の表示/並べ替え/フィルター/文字列検索の機能があり、いずれも Vue.js が大活躍してくれました。公開してから2ヶ月経ち、当初はわからなかったアニメーションのさせかたを知ったので追加しました。そして備忘録としてブログを書きたいと思います。

以下のリンクを参考にしたので合わせてどうぞ。公式ドキュメントの充実具合がさすがですね。

Vue.js でリストをアニメーションさせるには

Vue.js でリストをアニメーションさせるにはtransition-groupを使ってあげます。Vue の専門用語としては「トランジション」なようですが、どうもピンとこないので私は「アニメーション」と呼びたいと思います。

さて、このアニメーションですが、複数の実装方法が提供されているようです。今回は1番の、CSSクラスを使ったシンプルな方法を用います。

  • 自動適用される CSS クラスを使う
  • サードパーティの CSS アニメーションライブラリと組み合わせる
  • JavaScript フックを使う
  • サードパーティの JavaScript アニメーションライブラリと組み合わせる

さらに以下のことも可能です。文言は公式ドキュメントのママ。

本当の限界はあなたの想像力だけなのです。とさえ書いてありますね。格好良い!

ビフォー/アフターでみる transition-group コンポーネント

アニメーションを使わず即時に変化される場合と、アニメーションを使ってイイ感じに変化する場合の HTML を見比べてみます。

<div class="list">
	<div class="item" v-for="item in items">
		{{item.title}}
	</div>
</div>
<transition-group tag="div" class="list" name="vue-anime-list">
	<div class="item" v-for="item in items" :key="item">
		{{item.title}}
	</div>
</transition-group>

単純なリストレンダリングにはv-forを使ってあげれば OK でした。これをアニメーションさせるには、v-forを囲うようにtransition-groupを使います。

  • transition-groupにはtag属性をつけて、最終的なHTMLタグを指定する。省略するとspan
  • transition-groupにはname属性をつけて、CSS クラスの接頭語を指定する。省略するとv-
  • v-forの要素には、ユニークな、一意なkey属性をつける。これは必須。

これで以下の CSS クラスが使えるようになります。

  • .vue-anime-list-enter
  • .vue-anime-list-enter-to
  • .vue-anime-list-enter-active
  • .vue-anime-list-leave
  • .vue-anime-list-leave-to
  • .vue-anime-list-leave-active
  • .vue-anime-list-move

nameを省略した場合はv-enterなどになります。ただしtoに関してはバージョン 2.1.8 以降でのみ利用可能なようです。

enter, leave, move

任意に設定した接頭語に対して、enter, leave, move, to, active を組み合わせたクラスが追加されることがわかります。まずは、enter, leave, move の違いをみていきましょう。

enter はデータが挿入される前に追加されます。例えば、ページにアクセスした一番最初に vue が描画する時、検索条件が緩くなり合致するアイテムが増えた時などが当てはまります。leaveはデータが削除される前で、検索によって絞り込まれて除外されるアイテムなどに適用されます。moveは、並べ替えなどで位置が変化するときが当てはまります。

ゼヒ実際に試してみてほしいのですがmoveすごくないですか?公式ドキュメントでも魔法のように見えるかもしれませんがと書いていますね。なんでも FLIP というアニメーションテクニックを使っているようです。

無印, to, active

つづけて、to, active。これは本家の図を拝借しちゃいましょう。

https://jp.vuejs.org/v2/guide/transitions.html より

無印がアニメーションの開始時点を現し、toがアニメーションの終了時点を現します。それらを通してずっとactiveが付くといった考えです。これはenterleaveも共通です。

私は、リンクのaタグと:hoverで CSS アニメーションをつける場合と比べて理解しました。

a,
.vue-anime-enter-active{
	background: #000;
	border: 1px solid #000;
	border-radius: 3px;
	transition: background 0.5s ease 0s;
}

a:hover,
.vue-anime-enter-to{
	background: #fff;
}

こんなスタイルを書きますよね。ボタンをホバーするとフワっと背景色が変わる、みたいな。これと同じで CSS クラスに合わせてスタイルを当てていけばアニメーションが作れます。@keyframesを使う場合も基本的には同様。便利。すごい。さすが Vue.js。

CSS アニメーションライブラリを利用したい場合はカスタムクラスを用いると便利で、enter-active-classなどの属性を使うことができます。

<transition-group tag="div" class="list" name="vue-anime-list" enter-active-class="animated tada">
	<div class="item" v-for="item in items" :key="item">
		{{item.title}}
	</div>
</transition-group>

東京大学のアルバート・アイラー YouTube まとめでの実例

ここまでできれば、かなりのアニメーションが作れると思います。オンとオフのクラスさえ把握してしまえば、あとは CSS のことだけを考えれば良いわけですから。

最後に今回私が作ったスタイルを掲載して締めたいと思います。SCSS を使って Vue の接頭語をまとめてしまうと楽ですね。実際の動きは、冒頭の動画やページで確認してみてくださいね。

.vue-anime-list{
	&-enter-active{
		opacity: 0;
		transform: translateX(50px);
		transition: {
			property       : transform, opacity;
			duration       : 0.6s;
			timing-function: cubic-bezier(0.77, 0, 0.175, 1);
			delay:         : 0s;
		}
	}
	&-enter-to{
		opacity: 1;
		transform: translateX(0);
	}
	&-leave-active{
		opacity: 1;
		transform: translateX(0);
		transition: {
			property       : transform, opacity;
			duration       : 0.6s;
			timing-function: cubic-bezier(0.77, 0, 0.175, 1);
			delay:         : 0s;
		}
	}
	&-leave-to{
		opacity: 0;
		transform: translateX(50px);
	}
	&-move{
		transition: {
			property       : transform;
			duration       : 0.6s;
			timing-function: cubic-bezier(0.77, 0, 0.175, 1);
			delay          : 0s;
		}
	}
}

それではよいお年を。