【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
とすることは非推奨とされています。