RitoLabo

Vue.jsのtransitionアニメーションとSassの導入

  • 公開:
  • カテゴリ: JavaScript Vue.js
  • タグ: JavaScript,sass,Vue,transition,transition-group,Animation

Vue.jsをはじめとしたJavaScriptでのアプリケーション開発では、 より高価値なUX(ユーザー体験)を提供する為にリッチなUI(ユーザーインターフェース) を構築する機会も多いですが、その中でもアニメーションは良く用いられる手法の一つです。

また、Vue.jsではSFCにstyleも一緒に記述出来る利点がありますが、 それもやはりメンテナンス性を考え、SassなどのいわゆるCSS拡張言語で書いていきたいところです。

今回はVue.jsでのSassの導入と、transitionアニメーションを行っていきます。

アジェンダ
  1. 開発環境
  2. Vue.jsでSassを使う
    1. Sassのインストール
    2. Sassを記述する
  3. transitionコンポーネント
    1. クラス名の変更
  4. transition-groupコンポーネント

開発環境

今回の開発環境は以下の通りです。

  • Vue.js 2.x
  • Vue CLI 3.x
  • Node.js 12.x
  • npm 6.5

SFC(単一ファイルコンポーネント)を単体で動かすので、 インスタントプロトタイピングの為のグローバルアドオン「cli-service-global」 がインストールされている必要があります。

Vue CLIを使った開発環境構築の詳細については以下からも確認できます
Vue CLIでVue.jsの開発環境をサクッと構築する

Vue.jsでSassを使う

Nuxtやvue createなどのプロジェクト作成時に明示的にSassを入れている以外は基本的にSassは入っていないのでインストールする必要があります。

Sassのインストール

まずはSassをインストールします。プロジェクトルートへ移動し、以下のnpmコマンドでSASSコンパイラをインストールします。

# SASSコンパイラ インストール
npm install sass-loader node-sass

これでSassのインストールは完了です。

Sassを記述する

では実際にSassを使用してみます。style要素にlang属性を付与するだけでSassを記述する事が出来るようになります。

<style lang="scss">

これだけであとはSassをコンパイルしてくれるようになります。

今回は以下のようなコンポーネントを作成し、Sassを記述しました。

<template>
<div class="container">
<div class="items">
<div v-for="item in items" :key="item.id" class="item">{{ item.name }}</div>
</div>
</div>
</template>

<script>
export default {
name: "app",
data () {
return {
items: [
{ id: 1, name: 'item01' },
{ id: 2, name: 'item02' },
{ id: 3, name: 'item03' },
{ id: 4, name: 'item04' },
{ id: 5, name: 'item05' },
{ id: 6, name: 'item06' },
{ id: 7, name: 'item07' },
{ id: 8, name: 'item08' },
{ id: 9, name: 'item09' }
]
}
}
}
</script>

<style lang="scss" scoped>
$item-bg: #a0d8ef;

html {
height: 100%;
width: 100%;

body {
height: 100%;
margin: 0;
padding: 0;
position: relative;
text-align: center;
width: 100%;

.items {
left: 50%;
position: absolute;
top: 50%;
transform: translateY(-50%) translateX(-50%);
width: 330px;
}

.item {
animation: 1s ease 0s pulse infinite;
background: $item-bg;
border-radius: 4px;
float: left;
height: 75px;
margin: 5px;
width: 100px;
text-align: center;
}
}
}

@for $i from 1 through 9 {
.item {
&:nth-child(#{$i}) {
animation-delay: $i * (1s / 18);
}
}
}

@keyframes pulse {
0% {
background: $item-bg;
transform: scale(1);
}
25% {
background: darken($item-bg, 10%);
transform: scale(1.015);
}
50% {
background: $item-bg;
transform: scale(1);
}
}
</style>

以下のvueコマンドを叩いてブラウザから動作確認します。

# コンポーネント が設置されている場所まで移動
cd /path/to/dir

# app.vue 実行
vue serve app.vue --open

Sass動作確認

Sassがコンパイルされ、styleとして適用されている事が確認できました。
サンプルページ

transitionコンポーネント

Vue.jsでは、CSSトランジションやアニメーションの為にtransitionラッパーコンポーネントが提供されていて、これを用いると より簡潔に実装する事が出来ます。

具体的には、transitionコンポーネントでラップした要素に対して、その要素が追加や削除が行われた時にそのタイミングに応じたクラス名を付与(entering/leavingトランジション)します。 それに対してトランジッションを設定する事で、アニメーションを実現できます。

簡単な例として、ボタン押下でメッセージがフェードイン・フェードアウトするコードを書いてみます。

<template>
<div class="container">
<div class="msgBox">
<transition>
<p v-show="isShow" class="message">{{ message }}</p>
</transition>
</div>
<button @click="toggle">{{ btnLabel }}</button>
</div>
</template>

<script>
export default {
name: "app",
data () {
return {
message: 'Hello, world!',
show: false
}
},
computed: {
isShow () {
return this.show
},
btnLabel () {
return this.show ? 'hide' : 'show'
}
},
methods: {
toggle () {
this.show = !this.show
}
}
}
</script>

<style lang="scss" scoped>
.v-enter, .v-leave-to {
opacity: 0;
}
.v-enter-to, .v-leave {
opacity: 1;
}
.v-enter-active, .v-leave-active {
transition: opacity 300ms;
}

.container {
margin: 20px;

.msgBox {
height: 40px;
}

button {
padding: 5px 10px;
}
}
</style>

ポイントは以下です。

<transition>
<p v-show="isShow" class="message">{{ message }}</p>
</transition>

P要素をtransitionコンポーネントでラップしています。こうする事で、ラップされたP要素を対象として以下でstyleを設定します。

<style lang="scss" scoped>
.v-enter, .v-leave-to {
opacity: 0;
}
.v-enter-to, .v-leave {
opacity: 1;
}
.v-enter-active, .v-leave-active {
transition: opacity 300ms;
}
</style>

entering(表示時)とleaving(非表示時)に付与されるクラスに対してトランジッションを設定しフェードイン・フェードアウトを実現しています。

それぞれのタイミングで付与されるクラスについては以下の通りです。

.v-enter
挿入アニメーション開始時に付与
.v-enter-to
挿入アニメーション終了時に付与
.v-enter-active
挿入アニメーション開始〜終了まで付与
transitionをここで設定
.v-leave
削除アニメーション開始時に付与
.v-leave-to
削除アニメーション終了時に付与
.v-leave-active
削除アニメーション開始〜終了まで付与
transitionをここで設定

ではこれを動作させてみます。以下のvueコマンドを叩いて、ブラウザから確認します。

vue serve app.vue --open

transitionコンポーネントによるフェードイン・フェードアウト

transitionコンポーネントを用いたフェードイン・フェードアウトが実装できました。
サンプルページ

クラス名の変更

また、transitionコンポーネントにname属性を付与すれば、クラス名を[.xxx-enter]へ変更出来ます。 複数のアニメーションを定義する際に使えます。

例えば、今作成したのはフェードイン・フェードアウトなので、name属性に「fade」を指定した場合は以下の様になります。

<template>
<div class="container">
<div class="msgBox">
<transition name="fade">
<p v-show="isShow" class="message">{{ message }}</p>
</transition>
</div>
<button @click="toggle">{{ btnLabel }}</button>
</div>
</template>

<script>
export default {
name: "app",
data () {
return {
message: 'Hello, world!',
show: false
}
},
computed: {
isShow () {
return this.show
},
btnLabel () {
return this.show ? 'hide' : 'show'
}
},
methods: {
toggle () {
this.show = !this.show
}
}
}
</script>

<style lang="scss" scoped>
.fade-enter, .fade-leave-to {
opacity: 0;
}
.fade-enter-to, .fade-leave {
opacity: 1;
}
.fade-enter-active, .fade-leave-active {
transition: opacity 300ms;
}

.container {
margin: 20px;

.msgBox {
height: 40px;
}

button {
padding: 5px 10px;
}
}
</style>

サンプルページ

transition-groupコンポーネント

例えばリストのように複数の要素を扱ったりする場合は、transition-groupコンポーネントが使えます。 いちいち1つ1つの要素をtransitionコンポーネントでラップする必要が無いので便利です。

<template>
<div class="container">
<div class="item_wrapper">
<div class="items">
<transition-group name="list">
<div v-for="item in items" :key="item.id" class="item" v-show="item.show">{{ item.name }}</div>
</transition-group>
</div>
</div>
<div class="btns">
<button v-for="item in items" :key="item.id" class="switch" :class="{ hiding: !item.show }" @click="toggle(item.id)">{{ item.id }}</button>
</div>
</div>
</template>

<script>
export default {
name: "app",
data () {
return {
items: [
{ id: 1, name: 'item01', show: true },
{ id: 2, name: 'item02', show: true },
{ id: 3, name: 'item03', show: true },
{ id: 4, name: 'item04', show: true },
{ id: 5, name: 'item05', show: true },
{ id: 6, name: 'item06', show: true },
{ id: 7, name: 'item07', show: true },
{ id: 8, name: 'item08', show: true },
{ id: 9, name: 'item09', show: true }
],
}
},
methods: {
toggle (id) {
let target = this.items.find((item) => {
return item.id === id
})
target.show = !target.show
}
}
}
</script>

<style lang="scss" scoped>
$item-bg: #a0d8ef;
.item_wrapper {
width: 100%;
}

.items {
margin: auto;
width: 330px;
}

.item {
animation: 1s ease 0s pulse infinite;
background: $item-bg;
border-radius: 4px;
float: left;
height: 75px;
margin: 5px;
width: 100px;
text-align: center;
}

.btns {
width: 100%;
clear: both;
text-align: center;
}

.hiding {
background-color: #bfbfbf;
}

@for $i from 1 through 9 {
.item {
&:nth-child(#{$i}) {
animation-delay: $i * (1s / 18);
}
}
}

@keyframes pulse {
0% {
background: $item-bg;
transform: scale(1);
}

25% {
background: darken($item-bg, 10%);
transform: scale(1.015);
}

50% {
background: $item-bg;
transform: scale(1);
}
}

.list-enter, .list-leave-to {
opacity: 0;
}
.list-enter-to, .list-leave {
opacity: 1;
}
.list-enter-active, .list-leave-active {
transition: opacity 300ms ease-out;
}
</style>

使い方はtransitionコンポーネントと変わりません。まとめてラップしてあげるだけです。

ではこれを動作させてみます。以下のvueコマンドを叩いて、ブラウザから確認します。

vue serve app.vue --open

transition-groupコンポーネントによるフェードイン・フェードアウト

transition-groupコンポーネントを用いたフェードイン・フェードアウトが実装できました。
サンプルページ

まとめ

Vue.jsでは簡単にSassの導入が行えるので、styleを効率的に記述する為にもおすすめです。 transitionコンポーネントも、CSSトランジッションで消耗せずスマートに定義できるのでぜひ試してみてください。
サンプルコード