【Vue 2】スタイルとアニメーション
スタイル
データバインディング
インラインスタイル(style属性)にデータバインディングを行うには、スタイルの情報をもつオブジェクトを指定します。
CSS プロパティは、キャメルケースまたはクォートで囲ったケバブケースで指定します。
<div :style="styleObj">Hello World!</div>data: {
styleObj: {
color: 'red',
fontSize: '12px',
'font-weight': 'bold'
}
}<div style="color: red; font-size: 12px; font-weight: bold">Hello World!</div>直接スタイルオブジェクトを指定することもできます。
<div :style="{ color: fontColor, fontSize: `${fontSize}px`, 'font-weight': fontWeight }">
Hello World!
</div>data: {
fontColor: 'red',
fontSize: 12,
fontWeight: 'bold'
}複数のスタイルオブジェクトを指定するには、配列を用います。
<div :style="[ styleObj1, styleObj2 ]">Hello World!</div>data: {
styleObj1: {
color: 'red',
},
styleObj2: {
fontSize: '12px',
'font-weight': 'bold'
}
}アニメーション
CSS のtransitionプロパティによって、スタイルの変化によるアニメーションを設定することができます。
<div class="elm" :style="{ transform: `translateX(${x}px)` }"></div>data: {
x: 0,
},
methods: {
toLeft() {
if (x > 0) {
this.x -= 50
}
},
toRight() {
if (x < 400) {
this.x += 50
}
}
}.elm {
transition: transform 0.5s;
}クラス
データバインディング
クラスのデータバインディングは、単純にクラス名を指定します。
<div :class="className">Hello World!</div>data: {
className: 'class1'
}<div class="class1">Hello World!</div>複数のクラスをバインディングする場合は、配列を指定します。
<div :class="classes">Hello World!</div>data: {
classes: ['class1', 'class2', 'class3']
}<div class="class1 class2 class3">Hello World!</div>条件(Boolean)によってクラスを切り替えるには、クラス名と条件をもつオブジェクトを指定します。
オブジェクトのプロパティにクラス名を、値に条件を指定することで、条件がtrueのクラス名のみが展開されます。
<div :class="classObj">Hello World!</div>data: {
classObj: {
'class1': true,
'class2': false,
'class3': true
}
}<div class="class1 class3">Hello World!</div>条件が必要なものと必要でないものを併用する場合は、配列を用います。
<div :class="[ className, {'class2': condition2, 'class3': condition3 } ]">
Hello World!
</div>data: {
className: 'class1',
condition2: false,
condition3: true
}<div class="class1 class3">Hello World!</div>アニメーション
スタイル同様、CSS のtransitionプロパティによってクラスが付与されたことによるスタイル変化に対してアニメーションを設定します。
<p class="elm" :class="{ 'active': isActive }">Hello World!</p>data: {
isActive: false
},
methods: {
changeActive() {
this.isActive = !this.isActive
}
}.elm {
transition: color 0.5s;
}
.active {
color: red;
}より詳細なアニメーションの設定を行う場合は、animationプロパティを使用しますが、ここでの説明は割愛します。
transition
transition とは
ここでいうtransitionとは、CSS のプロパティではなく、Vue.js で実装されている<transition>を指します。
transitionの目的は、DOM の変化によるアニメーションを実現することです。
例えばv-ifやv-showによる要素の表示・非表示の際のアニメーションを実現します。
まずは簡単な例を見ていただきます。
<transition>
<p v-if="isActive">Hello World!</p>
</transition>data: {
isActive: true
},
methods: {
changeActive() {
this.isActive = !this.isActive
}
}.v-enter-active,
.v-leave-active {
transition: all 1s;
}
.v-enter,
.v-leave-to {
opacity: 0;
color: blue;
}
.v-enter-to,
.v-leave {
color: red;
}Hello World!
まずはv-ifなど、アニメーションの対象となる要素を<transition>で囲みます。
このとき、transitionの子は表示結果として 1 つである必要があります。
<transition>
<p v-if="isActive">active</p>
<p v-else>no active</p>
</transition><transition>
<p>active</p>
<p>no active</p>
</transition>次にアニメーション用の CSS プロパティを設定します。
transitionでは、要素の状態に応じて以下のクラスが対象の要素に付与されます。
クラス名は、Vue 2 と Vue 3 で一部違う点に注意してください。
| クラス(Vue 2) | クラス(Vue 3) | 説明 |
|---|---|---|
v-enter | v-enter-from | 表示開始時の状態を設定 |
v-enter-active | v-enter-active | 表示中のアニメーションを設定 |
v-enter-to | v-enter-to | 表示終了時の状態を設定 |
v-leave | v-leave-from | 非表示開始時の状態を設定 |
v-leave-active | v-leave-active | 非表示中のアニメーションを設定 |
v-leave-to | v-leave-to | 非表示終了時の状態を設定 |
各クラスの付与状態は以下のように移り変わります。
| タイミング | クラスの付与状態 |
|---|---|
| (非)表示開始時 | v-enter, v-enter-active(v-leave, v-leave-active) |
| (非)表示中 | v-enter-active, v-enter-to(v-leave-active, v-leave-to) |
| (非)表示終了時 | v-enter-active, v-enter-to(v-leave-active, v-leave-to) |
| (非)表示終了後 |
name
transitionにはnameを設定することができます。
<transition name="transaction1">
<p v-if="isActive">Hello World!</p>
</transition>nameを設定すると、transitionの各クラス名のv-の部分がnameに設定した値に変わります。
上の例だと、transition1-enterのようになります。
複数箇所にtransitionを使用する場合などは、nameで使い分けをします。
カスタムトランジッションクラス
transitionの各クラスは、それぞれ以下のプロパティで上書きすることができます。
| プロパティ(Vue 2) | プロパティ(Vue 3) | 説明 |
|---|---|---|
enter-class | enter-from-class | v-enter(v-enter-from)を上書き |
enter-active-class | enter-active-class | v-enter-activeを上書き |
enter-to-class | enter-to-class | v-enter-toを上書き |
leave-class | leave-from-class | v-leave(v-leave-from)を上書き |
leave-active-class | leave-active-class | v-leave-activeを上書き |
leave-to-class | leave-to-class | v-leave-toを上書き |
複数のクラスを指定する場合はスペースで区切ります。
<transition
enter-class="to-left to-bottom"
enter-active-class="show-animation"
leave-active-class="deshow-animation"
leave-to-class="to-right to-top"
>
<p v-if="isActive">Hello World!</p>
</transition>JavaScript フック
transitionによるアニメーションの各タイミングは、JavaScript による処理をイベントとしてバインドすることができます。
| イベント | 説明 |
|---|---|
before-enter | 表示開始時 |
enter | 表示中 |
after-enter | 表示終了時 |
enter-cancelled | 表示キャンセル時 |
before-leave | 非表示開始時 |
leave | 非表示中 |
after-leave | 非表示終了時 |
leave-cancelled | 非表示キャンセル時 |
<transition
@enter="enter"
@after-enter="afterEnter"
@leave="leave"
@after-leave="afterLeave"
>
<p v-if="isActive">Hello World!</p>
</transition>
<p>{{ status }}</p>data: {
isActive: true,
status: ''
}
methods: {
enter() {
this.status = '表示中'
},
afterEnter() {
this.status = ''
},
leave() {
this.status = '非表示中'
},
afterLeave() {
this.status = ''
}
}Hello World!
状態:
各メソッドの引数として、対象の要素を受け取ることができ、直接スタイルを変更することもできます。
methods: {
beforeEnter(el) {
el.style.opacity = 0
}
}仮に CSS を使わず、JavaScript 内ですべてのスタイルを変更する場合は、cssプロパティをfalseに設定します。
これによってパフォーマンスの向上が得られるようです。
<transition @before-enter="befoerEnter" :css="false">
<p v-if="isActive">Hello World!</p>
</transition>appear
appearはページの初回表示時のアニメーション実装で使用します。
<transition appear>
<p>Hello World</p>
</transition>.v-enter-active {
transition: all 1s;
}
.v-enter {
opacity: 0;
}appearは、appear-class、appear-active-class、appear-to-classと専用のカスタムトランジッションクラスがあります。
また JavaScript フックについても、before-appear、appear、after-appearと専用のものがあります。
duration
要素の表示、非表示までの時間を設定するためのプロパティです。 時間はミリ秒で設定します。
<transition :duration="1000">
<p v-if="isActive">Hello World!</p>
</transition>また表示(enter)と非表示(leave)の時間を別に設定することもできます。
<transition :duration="{ enter: 1000, leave: 2000 }">
<p v-if="isActive">Hello World!</p>
</transition>注意点として、このdurationと CSS で設定しているdurationは別の物です。
この違いは以下のプレビューで体感できると思います。
<transition :duration="3000">
<p v-if="isActive">Hello World!</p>
</transition>.v-enter-active,
.v-leave-active {
transition: all 1s;
}Hello World!
key, mode
v-ifとv-elseのように、複数の条件によって要素が切り替わる場合は、その要素を一意に識別するためのkeyの設定が必要になります。
keyの設定がない場合は、アニメーションが機能しません。
<transition>
<p v-if="isActive" key="active">active</p>
<p v-else key="no-active">no active</p>
</transition>active
プレビューを動かすとわかる通り、表示と非表示の処理は同時に実行されるため、アニメーション中はどちらの要素も表示されてしまいます。
これを解決するのがmodeになります。
modeには、表示 → 非表示の順で実行するin-outと、非表示 → 表示の順で実行するout-inを指定できます。
順に実行するためdurationの 2 倍の時間がかかります。
<transition mode="out-in">
<p v-if="isActive" key="active">active</p>
<p v-else key="no-active">no active</p>
</transition>active
transition-group
transition-groupは、v-forによるリスト表示などまとまった要素のアニメーションの実装で使用します。
<transition-group tag="p">
<span v-for="n in numbers" :key="n">{{ n }}</span>
</transition-group>data: {
numbers: [0, 1, 2]
},
methods: {
plus() {
this.numbers.shift()
this.numbers.push(this.numbers[this.numbers.length - 1] + 1)
}
}.v-enter-active,
.v-leave-active {
transition: all 1s;
}
.v-enter {
opacitry: 0;
transform: translateX(50%);
}
.v-leave-to {
opacity: 0;
transform: translateX(-50%);
}tagは、親となる要素を指定することができます。
上の例では以下のように展開されます。
<p>
<span>1</span>
<span>2</span>
<span>3</span>
</p>transition-groupでは特別に、v-modeというクラスが付与されます。
これは要素の入れ替わりがあった場合に付与されるクラスになります。
<transition-group tag="p">
<span class="number" v-for="n in numbers" :key="n">{{ n }}</span>
</transition-group>data: {
numbers: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
},
methods: {
reverse() {
this.numbers = this.numbers.reverse()
}
}.v-move {
transition: transform 1s;
}要素の変化はkeyの値によって判断されます。
例えば以下のようにインデックスをkeyに設定します。
<transition-group tag="p">
<span class="number" v-for="(n, index) in numbers" :key="index">{{ n }}</span>
</transition-group>これで同じ処理をしたとしてもアニメーションは実行されません。 なぜなら配列の値が変化したとしても、インデックス自体に変化がないためです。
そもそもインデックスをkeyとすることは非推奨とされています。