あっくんブログ Written by Akihiro Tsuji

Vue3のトランジションとアニメーション

Vue.js プログラミング

こんにちは、あっくんです。先日、たつの市に行ってきました。姫路からたつのまで車の運転の練習をさせていただきました。1週間に1回は車に乗るようにしています。運転するのが本当に怖いですが、乗るたびに怖さが減ってる感じがします。教えてくれる人に感謝です。ちなみに車庫入れは全然できていません。ここから頑張ります。今回はVue3のトランジションとアニメーションについてやっていきましょう。

トランジションで状態操作

Vue3では「状態を変化させる操作」が多いです。
トランジションは、スタイルシートの状態変化を扱うための機能を提供します。↓のように記述します。

<transition name="名前"> …….表示内容……… </transition>
Code language: HTML, XML (xml)

表示をON/OFFにする

トランジション利用のサンプルを作っていきましょう。

Viteでslot.appプロジェクトを作成しましょう。

開発するディレクトリでターミナルに↓を打ち込みましょう

npm init vite-app transition_app

cd transition_app
npm install

HelloWorld.vueを編集します。

<template> <div class="alert alert-primary"> <h2>{{ title }}</h2> <p>{{ message }}</p> <button class="btn btn-primary m-3" v-on:click="doAction"> {{ btn }} </button> <transition name="transit"> <p v-if="flg" class="alert alert-light h5">Transition context!</p> </transition> </div> </template> <script> export default { name: "HelloWorld", data() { return { title: "Trans&Anim", message: "This is Transition sample!", flg: true, btn: "Show/Hide", }; }, methods: { doAction() { this.flg = !this.flg; }, }, }; </script>
Code language: HTML, XML (xml)

App.vueを編集します

<template> <HelloWorld /> </template> <script> import HelloWorld from "./components/HelloWorld.vue"; export default { name: "App", components: { HelloWorld, }, }; </script>
Code language: HTML, XML (xml)

index.htmlを編集します。

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" href="/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Transition App</title> <!-- CSS only --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous" /> <!-- JavaScript Bundle with Popper --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous" ></script> </head> <body> <h1 class="bg-secondary text-white h4 p-3">Vue3 Transition</h1> <div class="container"> <div id="app"></div> </div> <script type="module" src="/src/main.js"></script> </body> </html>
Code language: HTML, XML (xml)
Image from Gyazo

↑npm run devをターミナルで打ち込んでボタンをクリックすると下のメッセージがON/OFFになります。

v-ifを使って、flgの値をチェックしています。これがtrueなら表示され、falseなら表示されません。

フェードイン/フェードアウト

トランジションはあらかじめ決められたルールにしたがってスタイルシートのクラスを定義します。これは、トランジションの名前の後に予約された名前入を追加する形で指定します。

Enter関係


Enter関係は消えていたものが画面に現れる時のトランジションです。

名前-enter-active
Enterトランジションがアクティブになったとき

名前-enter-from
Enterトランジションが開始されるとき

名前-enter
Enterトランジションがスタートしたとき

名前-enter-to
Enterトランジションが完了したとき

Leave関係

Leave関係は画面にあるものが消える時のトランジションです。

名前-leave-active
Leaveトランジションがアクティブになったとき

名前-leave-from
Leaveトランジションが開始されるとき

名前-leave
Leaveトランジションがスタートしたとき

名前-leave-to
Leaveトランジションが完了したとき

フェードイン/フェードアウト

<template> <div class="alert alert-primary"> <h2>{{ title }}</h2> <p>{{ message }}</p> <button class="btn btn-primary m-3" v-on:click="doAction"> {{ btn }} </button> <transition name="transit"> <p v-if="flg" class="alert alert-light h5">Transition context!</p> </transition> </div> </template> <script> export default { name: "HelloWorld", data() { return { title: "Trans&Anim", message: "This is Transition sample!", flg: true, btn: "Show/Hide", }; }, methods: { doAction() { this.flg = !this.flg; }, }, }; </script> <style> .trans { background-color: black; color: white; padding: 10px; font-size: 20pt; } .transit-enter-active { transition: opacity 0.5s; } .transit-leave-active { transition: opacity 5s; } .transit-enter { opacity: 0; } t .transit-enter-from { opacity: 0; } .transit-enter-to { opacity: 1; } .transit-leave { opacity: 1; } .transit-leave-to { opacity: 0; } </style>
Code language: HTML, XML (xml)
Image from Gyazo

↑npm run devをターミナルで打ち込むんで、ボタンをクリックすると、ゆっくりメッセージが消えていきます。もう一度クリックすると、ボタンが表れます。

クラスをチェック

アクティブ時の設定

.transit-enter-active { transition: opacity 0.5s; } .transit-leave-active { transition: opacity 5s;
Code language: CSS (css)

transitionは操作するスタイル名と経過時間を指定します。Enterのときにopacityを0.5秒で変化させます。Leaveのときは、opacityを5秒かけて変化させます。

Enterの設定

.transit-enter { opacity: 0; } .transit-enter-from { opacity: 0; } .transit-enter-to { opacity: 1; }
Code language: CSS (css)

Enterは、開始時点でopcityはゼロ、終了の時はゼロから1.0に変化する設定にしています。

Leaveの設定

.transit-leave { opacity: 1; } .transit-leave-to { opacity: 0; }
Code language: CSS (css)

Leaveha,開始時は1.0で、終了時はゼロです。

3つをセットで考えよう

トランジションのクラスは、Enter/Leaveと各activeの3つがセットになっています。activeのときにtransitionで経過時間を設定し、後はスタート時と終了時にopacityの値を用意します。

イベントを追加

トランジションに用意されているイベント用の属性があります。

Enter関係のイベント

before-enter
Enter開始直前

enter
Enter開始

after-enter
Enter終了後

enter-canselled
Enterキャンセル時

Leave関係のイベント

before-leave
Leave開始直前

leave
Leave開始

after-leave
Leave終了後

leave-canselled
Leaveキャンセル時

これらは<transition>タグにv-onを使って関数などにバインドして設定します。 例えば、v-on:before-enter=”beforeEnter”とすれば、Enter開始直前にbeforeEnter関数が実行されます。

イベントでメッセージを表示する。

<template> <div class="hello alert alert-primary"> <h2>{{ title }}</h2> <p>{{ message }}</p> <hr /> <button class="btn btn-primary m-3" v-on:click="doAction"> {{ btn }} </button> <transition name="transit" v-on:before-enter="startAction" v-on:before-leave="startAction" v-on:after-enter="endAction" v-on:after-leave="endAction" > <p v-if="flg" class="trans alert alert-light h5">Transition!</p> </transition> </div> </template> <script> export default { name: "HelloWorld", props: { title: String, }, data: function () { return { title: "Trans&Anim", message: "Transition Sample!", flg: true, btn: "Hide", }; }, methods: { doAction: function () { this.flg = !this.flg; }, startAction: function () { if (this.flg) { this.message = "現れます....."; } else { this.message = "消えます....."; } }, endAction: function () { if (this.flg) { this.btn = "Hide"; this.message = "現れました。"; } else { this.btn = "Show"; this.message = "消えました。"; } }, }, }; </script> <style> .trans { background-color: black; color: white; padding: 10px; font-size: 20pt; } .transit-enter-active { transition: opacity 2s; } .transit-leave-active { transition: opacity 2s; } .transit-enter { opacity: 0; } .transit-enter-from { opacity: 0; } .transit-enter-to { opacity: 1; } .transit-leave { opacity: 1; } .transit-leave-to { opacity: 0; } </style>
Code language: HTML, XML (xml)
Image from Gyazo

↑ボタンをクリックすると、「消えます….」とメッセージが変わり、完全に消えたところで「消えました」と表示されます。また、現れるときは、「Show」ボタンを押すとメッセージが「現れます。…..」

transformで動かす

transitionには「transform」という値が用意されており、これを利用することでタグの表示を移動したり、回転したり拡大縮小したりすることができます。↓のように記述します。

transform: ○○ 値;

平行移動


移動幅を示す値を指定します。例えば「100px」。

translateX
横方向に移動

translateY
縦方向に移動

拡大縮小


倍率を示す値を指定します。例えば、「0.5」とすれば半分に縮小し、「2.0」とすると2倍に拡大します。

scale
縦横等倍に拡大縮小

scaleX
横方向に拡大縮小

scaleY
縦方向に拡大縮小

回転


表示要素を時計回りに回転します。角度を示す値を指定します。例えば、「180deg」とすれば、半回転します。

rotateX
X軸を中心に回転

rotateY
Y軸を中心に回転

roteteZ
Z軸を中心に回転

ベースとなる表示を用意

<template> <div class="alert alert-primary"> <h2>{{ title }}</h2> <p>{{ message }}</p> <hr /> <button class="btn btn-primary m-3" v-on:click="doAction"> {{ btn }} </button> <transition name="transit"> <p v-if="flg" class="trans">Transition!</p> </transition> </div> </template> <script> export default { name: "HelloWorld", data: function () { return { title: "Trans&Anim", message: "Transition Sample!", flg: true, btn: "Show/Hide", }; }, methods: { doAction() { this.flg = !this.flg; }, }, }; </script> <style> .trans { background-color: black; color: white; padding: 10px; font-size: 20pt; } .transit-enter-active { transition: 1s; } .transit-leave-active { transition: 1s; opacity: 0.5; } </style>
Code language: HTML, XML (xml)
Image from Gyazo

↑ボタンをクリックするとメッセージがON/OFFされます。transitionに0.1sを指定しているので、1秒をかけて、ON/OFFするようにしてあります。

transformを試してみよう

styleタグ内にtransformを指定したクラスを追加して表示を確認しましょう。

平行移動をする

<template> <div class="alert alert-primary"> <h2>{{ title }}</h2> <p>{{ message }}</p> <hr /> <button class="btn btn-primary m-3" v-on:click="doAction"> {{ btn }} </button> <transition name="transit"> <p v-if="flg" class="trans">Transition!</p> </transition> </div> </template> <script> export default { name: "HelloWorld", data: function () { return { title: "Trans&Anim", message: "Transition Sample!", flg: true, btn: "Show/Hide", }; }, methods: { doAction() { this.flg = !this.flg; }, }, }; </script> <style> .trans { background-color: black; color: white; padding: 10px; font-size: 20pt; } .transit-enter-active { transition: 1s; } .transit-leave-active { transition: 1s; opacity: 0.5; } .transit-enter, .transit-leave-to { transform: translateX(200px) translateY(-200px); opacity: 0.1; } </style>
Code language: HTML, XML (xml)
Image from Gyazo

↑ボタンをクリックするとメッセージが右上に移動しながら消えます。

ここではtranslateXとtranslateYを使って、右に200ドット、上に200ドット移動させています。

拡大縮小

<template> <div class="alert alert-primary"> <h1>{{ title }}</h1> <p>{{ message }}</p> <button class="btn btn-primary mb-3" v-on:click="doAction"> Show/Hide </button> <transition name="transit"> <p v-if="flg" class="trans">Transition!</p> </transition> </div> </template> <script> export default { name: "HelloWorld", data: function () { return { title: "Trans&Anim", message: "Transition Sample!", flg: true, btn: "Hide", }; }, methods: { doAction() { this.flg = !this.flg; }, }, }; </script> <style> .trans { background-color: black; color: white; padding: 10px; font-size: 20pt; } .transit-enter-active { transition: 1s; } .transit-leave-active { transition: 1s; opacity: 0.5; } .transit-enter-from, .transit-leave-to { transform: scale(5); opacity: 0.1; } </style>
Code language: HTML, XML (xml)
Image from Gyazo

↑ボタンをクリックすると、メッセージが拡大して消えます。再度クリックすると大きなサイズの状態から次第に縮小し、元のサイズに戻ります。

回転する

<template> <div class="alert alert-primary"> <h1>{{ title }}</h1> <p>{{ message }}</p> <button class="btn btn-primary mb-3" v-on:click="doAction"> Show/Hide </button> <transition name="transit"> <p v-if="flg" class="trans">Transition!</p> </transition> </div> </template> <script> export default { name: "HelloWorld", data: function () { return { title: "Trans&Anim", message: "Transition Sample!", flg: true, btn: "Hide", }; }, methods: { doAction() { this.flg = !this.flg; }, }, }; </script> <style> .trans { background-color: black; color: white; padding: 10px; font-size: 20pt; } .transit-enter-active { transition: 1s; } .transit-leave-active { transition: 1s; opacity: 0.5; } .transit-enter-from, .transit-leave-to { transform: rotateZ(360deg); opacity: 0.1; } </style>
Code language: HTML, XML (xml)
Image from Gyazo

↑ボタンをクリックすると、時計回りにメッセージが回転して消えます。再度クリックすると、逆周りに回転しながら現れます。

degはdegreeのことで、1回転=360で計算した角度になります。

キーフレームによる複雑なアニメーション

animaiton: キーフレーム 経過時間;

このanimationはキーフレームと呼ばれるものを使って要素を動かします。キーフレームは動きの開始から終了までの間に「キー」と呼ばれるものを用意し、各キーの状態に変化しながら動かしていく働きをします。

@keyframes 名前 {  0% {  ………開始時の設定………  }  ○○% {  ………指定したパーセンテージでの状態設定..  }  ………必要なだけ用意…….  100% {   ……….完了時の設定……….  } }

ジグザグに動く

<template> <div class="alert alert-primary"> <h1>{{ title }}</h1> <p>{{ message }}</p> <button class="btn btn-primary mb-3" v-on:click="doAction"> Show/Hide </button> <transition name="transit"> <p v-if="flg" class="trans">Transition!</p> </transition> </div> </template> <script> export default { name: "HelloWorld", data: function () { return { title: "Trans&Anim", message: "Transition Sample!", flg: true, btn: "Hide", }; }, methods: { doAction() { this.flg = !this.flg; }, }, }; </script> <style> .trans { background-color: black; color: white; padding: 10px; font-size: 20pt; } .transit-leave-active { animation: anim 3s; } .transit-enter-active { animation: anim 2.5s reverse; } @keyframes anim { 0% { transform: translateX(0px) translateY(0px) rotateZ(0deg); opacity: 1; background-color: #ddf; } 25% { transform: translateX(250px) translateY(0px) rotateZ(0deg); opacity: 1; background-color: #fdd; } 50% { transform: translateX(0px) translateY(-100px) rotateZ(540deg); opacity: 1; background-color: #dfd; } 75% { transform: translateX(250px) translateY(-100px) rotateZ(540deg); opacity: 1; background-color: #fdf; } 100% { transform: translateX(0px) translateY(-200px) rotateZ(1080deg); opacity: 0; background-color: #ffd; } } </style>
Code language: HTML, XML (xml)
Image from Gyazo

↑クリックするとメッセージがジグザグに右に左にとジグザグに動きながら、回転したり、背景色を変えたりしてアニメーションします。再度クリックすると逆に再生して元に戻ります。

animationにanimを指定することで、animキーフレームによるアニメーションが行われるようになっています。transit-enter-activeにある「reverse」は逆再生のことです。

*color, font-size, border-widthなどもアニメーションにできます。

ここまで読んでいただいてありがとうございます。
今回はtransitionを使ってのアニメーションのやり方が分かりました。CSSで勉強していたので、頭に入りやすかったです。
掌田津耶乃さんのVue.js3超入門で勉強をしています。語りかけるような文章ですごいわかりやすいです。本当におすすめです。