Vue.js では、要素を追加/更新/削除したときのアニメーションを、簡単に実装できるような機能が用意されています。
ありがたいことに、公式のドキュメントにバッチリとまとまっていますが、それを要約しつつパパッと今日から始められるようにと、ライトに端折って紹介します。
シンプルな実装
Vue が提供しているアニメーション機能は、かなり多機能で美味しい仕様になっていますが、とりあえず一番簡単なアプローチで実装してみます。
ポイント
-
<transition name="your-anim-name">
で、v-if
やv-show
が指定された要素をラップする -
name
属性がclass
名のプレフィクスになる - 挿入(enter)/削除(leave)時のアニメーションの進捗を補足して、それに応じた
class
を自動で切り替える - CSSの
transition
とanimation
どちらも受け付ける
コード
<div id="app">
<transition name="fade">
<img v-show="show" class="image" src="https://source.unsplash.com/1600x900/?nature,water" alt="placeholder">
</transition>
<button class="button" id="button" @click="toggle">{{show ? 'leave' : 'enter'}}</button>
</div>
// enter の継続状態。トランジションがはじまるフェーズ中に適用。
// 要素が挿入される前に追加され、トランジション/アニメーションが終了すると削除
// 登場シーンの期間、遅延、イージングを定義
.fade-enter-active {
transition: all 1s cubic-bezier(0.19, 1, 0.22, 1);
// animation: fade .5s;
}
// leave の継続状態。トランジションが終わるフェーズ中に適用
// leave がトリガされるとき追加され、トランジション/アニメーションが終了すると削除
// 削除シーンの期間、遅延、イージングを定義
.fade-leave-active {
transition: all 1s cubic-bezier(0.895, 0.03, 0.685, 0.22);
// animation: fade .5s reverse;
}
// enter の開始状態。
// 要素が挿入される前に適用され、要素が挿入された 1 フレーム後に削除
// 要素の登場シーンのスタイルがここ
.fade-enter {
opacity: 0;
transform: translateX(-100%);
}
// leave の終了状態
// トランジションの終了がトリガされた後に (同時に v-leave 削除) 1フレーム追加
// トランジション/アニメーションが終了すると削除
// 要素の削除シーンのスタイルがここ
.fade-leave-to {
opacity: 0;
transform: translateX(100%);
}
const root = new Vue({
data: function() {
return {
show: false
}
},
methods: {
toggle() {
this.show = !this.show
}
},
});
root.$mount( '#app' );
初期描画時に実行する
初期描画時に実行するには、 appear
属性を付与します。
<transition appear name="fade">
<el></el>
</transition>
発展した使い方
よく使う、すこし高度な実装を紹介します。
いくつかの要素を入れ替えるアニメーション
表示要素を入れ替えるようなアニメーションには key
属性を利用します。
mode
属性でイベントが発火するタイミングをコントロールします。
削除する要素のアニメーションの完了を待ったあと、展開する要素のアニメーションを開始するように、 out-in
を指定します。
<transition name="fade" mode="out-in">
<button :key="show" class="button" id="button" @click="toggle">{{show ? 'leave' : 'enter'}}</button>
</transition>
JavaScriptのフック
class
が自動切替えされるタイミングで、任意のコールバック関数を定義できます。これで、jQuery や TweenMax などのライブラリと連携をとって、より複雑なアニメーションを組み込めます。
CSSアニメーションを使わない場合は、:css="false"
を記述し、enter(el, done)
leave(el, done)
の第二引数で、アニメーションの完了を教えてあげます。
<transition
@enter="enter"
@leave="leave"
:css="false"
>
<el></el>
</transition>
const root = new Vue({
methods: {
//
enter(el, done) {
TweenMax.fromTo(el, 1, {
x: '-100%',
rotationX: 180,
}, {
x: '0%',
rotationX: 0,
scale: 1,
onComplete: done,
ease: Expo.easeOut,
})
},
leave(el, done) {
TweenMax.to(el, .5, {
x: '100%',
rotationX: 360,
scale: .1,
onComplete: done,
ease: Expo.easeIn,
})
}
},
});
複数のアイテムのアニメーション
これまでは単一の要素を描画するためのアニメーションでしたが、複数の要素が追加/削除されるような場合のアニメーションを実装することもできます。
ポイント
-
<transition-group>
で要素をラップする -
key
をそれぞれの要素に指定する -
.*-move
で位置の変化を制御する
コード
<div id="app">
<div class="field has-addons">
<p class="control">
<button class="button" @click="toggle">
{{show ? 'leave' : 'enter'}}
</button>
</p>
<p class="control">
<button class="button" @click="add">
add
</button>
</p>
<p class="control">
<button class="button" @click="shuffle">
shuffle
</button>
</p>
</div>
<div class="field">
<label class="label">Image Query</label>
<div class="control">
<input class="input" type="text" placeholder="Text input" v-model="query">
</div>
<p class="help is-info">Enter the keyword of the image you want to call.</p>
</div>
<transition-group name="fade" tag="ul" class="columns is-mobile is-multiline is-gapless">
<li class="column is-one-third" v-show="show" v-for="item in items" :key="item.src">
<img :src="item.src" alt="placeholder">
</li>
</transition-group>
</div>
let baseImageUrl = 'https://source.unsplash.com/1600x900/?';
const root = new Vue({
data: function() {
return {
show: false,
items: [
{
id: this.counter,
src: baseImageUrl + 'nature,water'
},
],
query: '',
counter: 0,
}
},
methods: {
toggle() {
this.show = !this.show
},
add() {
// 画像ロードするの待って
let img = new Image();
let src = baseImageUrl + this.query;
img.onload = () => {
this.items.push({
src: src
});
};
img.src = src;
},
shuffle() {
this.items = _.shuffle(this.items)
}
}
});
root.$mount( '#app' );
// 位置の変化を検知する *-move
.fade-move {
transition: all 1s cubic-bezier(0.895, 0.03, 0.685, 0.22);
}
.fade-enter-active {
// easeOutExpo
transition: all 1s cubic-bezier(0.19, 1, 0.22, 1);
}
.fade-leave-active {
// easeInQuart
transition: all 1s cubic-bezier(0.895, 0.03, 0.685, 0.22);
}
.fade-enter {
opacity: 0;
transform: translateX(-100%);
}
.fade-leave-to {
opacity: 0;
transform: translateX(100%);
}
複数の要素の位置関係を把握して、要素の追加や並び替えに反応、適当な位置へアニメーションしてくれます。ソートUIのアニメーションを簡単に書くことができますね。
Vue.jsのアニメーションすごい
ざっくりとアニメーションまわりを紹介しましたが、Vue.jsのアニメーション機能は非常に実践的で整っている印象です。詳しくはドキュメントやサンプルを参照してください。
他にも機能を掘り下げていくといろんなことができそうなので、実験して模索していきたいですね。🍟
おわります。