LoginSignup
17
35

More than 5 years have passed since last update.

《JavaScript》Vue.js で今日からはじめるアニメーション。

Last updated at Posted at 2017-12-19

Vue.js では、要素を追加/更新/削除したときのアニメーションを、簡単に実装できるような機能が用意されています。

ありがたいことに、公式のドキュメントにバッチリとまとまっていますが、それを要約しつつパパッと今日から始められるようにと、ライトに端折って紹介します。

シンプルな実装

Vue が提供しているアニメーション機能は、かなり多機能で美味しい仕様になっていますが、とりあえず一番簡単なアプローチで実装してみます。

ポイント

  1. <transition name="your-anim-name"> で、v-ifv-show が指定された要素をラップする
  2. name 属性が class 名のプレフィクスになる
  3. 挿入(enter)/削除(leave)時のアニメーションの進捗を補足して、それに応じた class を自動で切り替える
  4. CSSの transitionanimation どちらも受け付ける

コード

<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,
        })
      }
    },
  });

複数のアイテムのアニメーション

これまでは単一の要素を描画するためのアニメーションでしたが、複数の要素が追加/削除されるような場合のアニメーションを実装することもできます。

ポイント

  1. <transition-group> で要素をラップする
  2. key をそれぞれの要素に指定する
  3. .*-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のアニメーション機能は非常に実践的で整っている印象です。詳しくはドキュメントやサンプルを参照してください。

他にも機能を掘り下げていくといろんなことができそうなので、実験して模索していきたいですね。🍟

おわります。

17
35
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
35