Vue.jsミニハンズオン(TODOリストにアニメーションをつける)
AngularでもReactでもriot.jsでも満足できなかったひとに、ぴったりフィットなJSフレームワーク「Vue.js」のざっくりハンズオン第2回目です。
第1回目は「Vue.jsミニハンズオン(TODOリスト作成)」をご覧ください。
このハンズオンではnode.jsのパッケージは使わず、Google ChromeとテキストエディタがあればOKです。
Vue.jsミニハンズオンのシリーズは以下を公開しています。
今回の目標
目標は第1回目で作ったシンプルなTODOリストにアニメーションをつけることです。
まずはシンプルなアニメーションを作ってみる
TODOリストにアニメーションをつける方法がまだわからないので、
ひとまずアニメーションの付け方を学んでいきます。
ボタンでp
要素を表示/非表示してみます。
必要な知識は下記。
- Vue.jsが持っているカスタム要素、
transition
要素を使う -
transition
要素のname
属性でアニメーション用CSSの接頭辞を指定する - 表示/非表示を管理するデータを用意する(ここでは
show
) -
p
要素にv-if
属性と表示/非表示を管理するデータを関連付ける
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>まずはシンプルなアニメーションを作ってみる</title>
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity .5s
}
.fade-enter, .fade-leave-to {
opacity: 0
}
</style>
</head>
<body>
<div id="demo">
<button v-on:click="show = !show">
Toggle
</button>
<transition name="fade">
<p v-if="show">hello</p>
</transition>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="./app_animation.js"></script>
</body>
</html>
const vm = new Vue({
el: '#demo',
data: {
show: true
}
})
ボタンをクリックするとshow
のtrue, falseという値がひっくり返るため、
p要素はv-if
ディレクティブによって表示/非表示が切り替わります。
v-if
ディレクティブは表示/非表示するというより、要素を追加/削除するものなんですが、
transition
要素をつかうことでアニメーションを付加できます。
アニメーションはCSSで設定する(JavaScriptでもできるけども)
上のstyle
要素内には
.fade-enter-active, .fade-leave-active
や
.fade-enter, .fade-leave-to
という、これはいつ使うのかな?と思うようなクラス名がありました。
これはアニメーション時にVue.jsが要素に付加する特定のクラス名です。
transition
要素のname
属性値(上のhtmlではfade
)を接頭辞としてクラス名を作り出します。
- fade-enter
- fade-enter-to
- fade-enter-active
- fade-leave
- fade-leave-to
- fade-leave-active
これらのクラス名を使ってCSSでアニメーションを指定することで
自由なアニメーションの設定ができます。
Vue.js公式サイトに掲載されているのアニメーションの流れの図を見てみてください。
Enterは非表示→表示、Leaveは表示→非表示のときを表しています。
.fade-enter-active, .fade-leave-active
のように-active
とついているクラスは
アニメーションが動作する間ずっと要素に付加されるクラスです。
これらにはtransition
,animation
プロパティを指定することになります。
.fade-enter-active, .fade-leave-active {
transition: opacity .5s
}
そして.fade-enter, .fade-leave-to
のように上の図の左端と右端にあたる「非表示の状態」の見た目をopacity: 0;
などで指定します。
.fade-enter, .fade-leave-to {
opacity: 0
}
animationプロパティを使ってみる
上記の例ではtransition
プロパティで比較的単純なアニメーションを表示しました。
animation
プロパティを使うともう少し複雑なアニメーションが表示できます。
animation.htmlにのbody
要素内に下記を追加します。
<div id="example-2">
<button @click="show = !show">Toggle show</button>
<transition name="bounce">
<p v-if="show">Look at me!</p>
</transition>
</div>
app_animation.jsに下記スクリプトを追加します。
new Vue({
el: '#example-2',
data: {
show: true
}
})
また、animation.htmlのstyle
要素内に下記を追加します。
#example-2 p {
background-color: pink;
}
.bounce-enter-active {
animation: bounce-in .5s;
}
.bounce-leave-active {
animation: bounce-in .5s reverse;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
追加したhtmlとjsはほぼ最初に入れたものと同じです。
transition
要素name属性値だけ違います。
CSSは-active
とついているクラスの両方にanimation
プロパティを設定し、
@keyframes
ルールでbounce-in
と名前をつけたアニメーションを呼び出しています。
注目するのはbounce-leave-active
というクラスのanimation
プロパティにはreverse
という値がついてることです。これはanimation-direction
プロパティの値で、アニメーションの向きを逆にするという指定になります。
ここのアニメーションの指定では下記の用に変化します。
- Enter時:大きさ0倍→1.5倍→1倍
- Leave時:大きさ1倍→1.5倍→0倍
animate.cssを使いたい
いろんな種類のアニメーションが設定されているanimate.cssを使いたい!
そんな要望にVue.jsは応えてくれます。
animation.htmlのhead
要素内に下記要素を追記します。
<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">
animation.htmlのbody
要素内に下記要素追記します。
<div id="example-3">
<button @click="show = !show">
Toggle render
</button>
<transition
name="custom-classes-transition"
enter-active-class="animated tada"
leave-active-class="animated bounceOutRight"
>
<p v-if="show">hello</p>
</transition>
</div>
app_animation.jsに下記スクリプトを追加します。
new Vue({
el: '#example-3',
data: {
show: true
}
})
transition
要素の開始タグが重要なところです。
<transition
name="custom-classes-transition"
enter-active-class="animated tada"
leave-active-class="animated bounceOutRight"
>
name
属性値custom-classes-transition
はanimate.cssのような名前があらかじめ決まっているクラスを使用するときに指定します。
enter-active-class
leave-active-class
といった属性は先に出した図のまさにその場所にあてはまるクラスを指定します。
ここではanimate.cssで固定で指定するanimated
というクラスとアニメーションの種類を指定するtada
、'bounceOutRight'などのクラスを指定しています。
animate.cssのいろんなクラスに差し替えて試してみると面白いです。
基本的にはIn
,Out
がクラス名に入っているものを利用するのがスムーズに利用できるようです。
察しの良い方は気づくかもしれませんがほかの状態の属性も存在します。
なにかしら複雑な指定をするときには使えるかも。
- enter-class
- enter-active-class
- enter-to-class
- leave-class
- leave-active-class
- leave-to-class
リストのアニメーション
これまでは1つの項目をアニメーションさせることを見てきましたがリストに項目を追加・削除するアニメーションはどのように実現できるでしょうか。
ここで初めてtransition-group
要素がでてきます。
animation.htmlに下記を追加します。
name
属性値はtransition
要素と同じくクラス名の接頭辞になります。
tag
属性値はtransition-group
が置き換わる要素タイプになります。何も指定しなければspan
要素が表示されます。
v-for
属性が入っているspan
要素は要素を一意に指定できるものとして
key
を指定する必要があります。
ここはお忘れなく。
<div id="list-complete-demo" class="demo">
<button v-on:click="add">Add</button>
<button v-on:click="remove">Remove</button>
<transition-group name="list-complete" tag="p">
<span
v-for="item in items"
v-bind:key="item"
class="list-complete-item"
>
{{ item }}
</span>
</transition-group>
</div>
app_animation.jsに下記を追加します。
これは単純に配列の操作をしているだけです。
new Vue({
el: '#list-complete-demo',
data: {
items: [1,2,3,4,5,6,7,8,9],
nextNum: 10
},
methods: {
randomIndex: function () {
return Math.floor(Math.random() * this.items.length)
},
add: function () {
this.items.splice(this.randomIndex(), 0, this.nextNum++)
},
remove: function () {
this.items.splice(this.randomIndex(), 1)
},
}
})
また、animation.htmlのstyle
要素内に下記を追加します。
.list-complete-item {
transition: all 1s;
display: inline-block;
margin-right: 10px;
}
.list-complete-enter, .list-complete-leave-to
/* .list-complete-leave-active for below version 2.1.8 */ {
opacity: 0;
transform: translateY(30px);
}
.list-complete-leave-active {
position: absolute;
}
AddボタンとRemoveボタンをクリックすると要素がスルッと動いて追加・削除されます。
どうやらこのスムーズなアニメーションにはコツがあるようです。
- いままで
-active
に指定していたtransition
プロパティやanimation
プロパティを要素の通常状態に入れ込む
.list-complete-item {
transition: all 1s; /* 重要 */
display: inline-block;
margin-right: 10px;
}
-
-leave-active
にはposition: absolute;
を入れる
.list-complete-leave-active {
position: absolute;
}
アニメーションする要素はposition: static;
のレイアウト状態からはずすことで
他の要素の動きをスムーズにするということのようです。
transform
プロパティのtranslate
関数で位置を動かしても実際の要素の場所は変わってませんもんね。
TODOリストにアニメーションをつける
ようやくここまでやってきました。あとは前回のソースにいままで学んだことを入れ込むだけです。
なんとjsファイルを編集する必要はありません。htmlとcssだけの編集で済みます。
<ul>
<li v-for="item in items">
<label v-bind:class="{ done: item.isChecked }">
<input type="checkbox" v-model="item.isChecked" v-on:change="saveTodo"> {{ item.title }}
</label>
</li>
</ul>
↓
<transition-group name="list-complete" tag="ul">
<li v-for="item in items" v-bind:key="item" class="list-complete-item">
<label v-bind:class="{ done: item.isChecked }">
<input type="checkbox" v-model="item.isChecked" v-on:change="saveTodo"> {{ item.title }}
</label>
</li>
</transition-group>
css部分に下記を入れます。
.list-complete-item {
transition: all 1s;
}
.list-complete-enter, .list-complete-leave-to
/* .list-complete-leave-active for below version 2.1.8 */ {
opacity: 0;
transform: translateX(30px);
}
.list-complete-leave-active {
position: absolute;
}
Vue.js公式のアニメーションのガイドはかなりボリュームがあります
Vue.js公式のアニメーションのガイドはについては下記をご覧ください。
このハンズオンの例の大半はこのガイドから持ってきました。
そしてかなりはしょっています。しっかり学びたい方はこちらでも学んでみてください。
とても良くまとまっていて勉強しやすいですよ。