Help us understand the problem. What is going on with this article?

Vue.jsミニハンズオン(TODOリストにアニメーションをつける)

More than 1 year has passed since last update.

Vue.jsミニハンズオン(TODOリストにアニメーションをつける)

AngularでもReactでもriot.jsでも満足できなかったひとに、ぴったりフィットなJSフレームワーク「Vue.js」のざっくりハンズオン第2回目です。

第1回目は「Vue.jsミニハンズオン(TODOリスト作成)」をご覧ください。

このハンズオンではnode.jsのパッケージは使わず、Google ChromeとテキストエディタがあればOKです。

Vue.jsミニハンズオンのシリーズは以下を公開しています。

  1. Vue.jsミニハンズオン(TODOリスト作成)
  2. Vue.jsミニハンズオン(TODOリストにアニメーションをつける)
  3. Vue.jsミニハンズオン(TODOリストをコンポーネント化する)

今回の目標

目標は第1回目で作ったシンプルなTODOリストにアニメーションをつけることです。

まずはシンプルなアニメーションを作ってみる

TODOリストにアニメーションをつける方法がまだわからないので、
ひとまずアニメーションの付け方を学んでいきます。

ボタンでp要素を表示/非表示してみます。
必要な知識は下記。

  • Vue.jsが持っているカスタム要素、transition要素を使う
  • transition要素のname属性でアニメーション用CSSの接頭辞を指定する
  • 表示/非表示を管理するデータを用意する(ここではshow
  • p要素にv-if属性と表示/非表示を管理するデータを関連付ける
animation.html
<!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>
app_animation.js
const vm = new Vue({
  el: '#demo',
  data: {
    show: true
  }
})

CODEPEN

ボタンをクリックすると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要素内に下記を追加します。

animation.html
<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に下記スクリプトを追加します。

app_animation.js
new Vue({
  el: '#example-2',
  data: {
    show: true
  }
})

また、animation.htmlのstyle要素内に下記を追加します。

animation.html
#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);
  }
}

CODEPEN

追加した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要素内に下記要素を追記します。

animation.html
<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">

animation.htmlのbody要素内に下記要素追記します。

animation.html
<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に下記スクリプトを追加します。

app_animation.js
new Vue({
  el: '#example-3',
  data: {
    show: true
  }
})

CODEPEN

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を指定する必要があります。
ここはお忘れなく。

animation.html
<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に下記を追加します。
これは単純に配列の操作をしているだけです。

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要素内に下記を追加します。

animation.html
.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;
}

CODEPEN

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だけの編集で済みます。

todo.html
  <ul>
    <li v-for="item in items">
      <label v-bind:class="{ done: item.isChecked }">
        <input type="checkbox" v-model="item.isChecked"> {{ item.title }}
      </label>
    </li>
  </ul>

todo.html
  <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"> {{ item.title }}
      </label>
    </li>
  </transition-group>

css部分に下記を入れます。

todo.html
.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;
}

CODEPEN

Vue.js公式のアニメーションのガイドはかなりボリュームがあります

Vue.js公式のアニメーションのガイドはについては下記をご覧ください。
このハンズオンの例の大半はこのガイドから持ってきました。
そしてかなりはしょっています。しっかり学びたい方はこちらでも学んでみてください。
とても良くまとまっていて勉強しやすいですよ。

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした