vue.svg

【Vue 2】スタイルとアニメーション

Vue 2

スタイル

データバインディング

インラインスタイル(style属性)にデータバインディングを行うには、スタイルの情報をもつオブジェクトを指定します。 CSS プロパティは、キャメルケースまたはクォートで囲ったケバブケースで指定します。

HTML
<div :style="styleObj">Hello World!</div>
JavaScript
data: {
  styleObj: {
    color: 'red',
    fontSize: '12px',
    'font-weight': 'bold'
  }
}
結果
<div style="color: red; font-size: 12px; font-weight: bold">Hello World!</div>

直接スタイルオブジェクトを指定することもできます。

HTML
<div :style="{ color: fontColor, fontSize: `${fontSize}px`, 'font-weight': fontWeight }">
  Hello World!
</div>
JavaScript
data: {
  fontColor: 'red',
  fontSize: 12,
  fontWeight: 'bold'
}

複数のスタイルオブジェクトを指定するには、配列を用います。

HTML
<div :style="[ styleObj1, styleObj2 ]">Hello World!</div>
JavaScript
data: {
  styleObj1: {
    color: 'red',
  },
  styleObj2: {
    fontSize: '12px',
    'font-weight': 'bold'
  }
}

アニメーション

CSS のtransitionプロパティによって、スタイルの変化によるアニメーションを設定することができます。

HTML
<div class="elm" :style="{ transform: `translateX(${x}px)` }"></div>
JavaScript
data: {
  x: 0,
},
methods: {
  toLeft() {
    if (x > 0) {
      this.x -= 50
    }
  },
  toRight() {
    if (x < 400) {
      this.x += 50
    }
  }
}
CSS
.elm {
  transition: transform 0.5s;
}

クラス

データバインディング

クラスのデータバインディングは、単純にクラス名を指定します。

HTML
<div :class="className">Hello World!</div>
JavaScript
data: {
  className: 'class1'
}
結果
<div class="class1">Hello World!</div>

複数のクラスをバインディングする場合は、配列を指定します。

HTML
<div :class="classes">Hello World!</div>
JavaScript
data: {
  classes: ['class1', 'class2', 'class3']
}
結果
<div class="class1 class2 class3">Hello World!</div>

条件(Boolean)によってクラスを切り替えるには、クラス名と条件をもつオブジェクトを指定します。 オブジェクトのプロパティにクラス名を、値に条件を指定することで、条件がtrueのクラス名のみが展開されます。

HTML
<div :class="classObj">Hello World!</div>
JavaScript
data: {
  classObj: {
    'class1': true,
    'class2': false,
    'class3': true
  }
}
結果
<div class="class1 class3">Hello World!</div>

条件が必要なものと必要でないものを併用する場合は、配列を用います。

HTML
<div :class="[ className, {'class2': condition2, 'class3': condition3 } ]">
  Hello World!
</div>
JavaScript
data: {
  className: 'class1',
  condition2: false,
  condition3: true
}
結果
<div class="class1 class3">Hello World!</div>

アニメーション

スタイル同様、CSS のtransitionプロパティによってクラスが付与されたことによるスタイル変化に対してアニメーションを設定します。

HTML
<p class="elm" :class="{ 'active': isActive }">Hello World!</p>
JavaScript
data: {
  isActive: false
},
methods: {
  changeActive() {
    this.isActive = !this.isActive
  }
}
CSS
.elm {
  transition: color 0.5s;
}
.active {
  color: red;
}
Hello World

より詳細なアニメーションの設定を行う場合は、animationプロパティを使用しますが、ここでの説明は割愛します。

transition

transition とは

ここでいうtransitionとは、CSS のプロパティではなく、Vue.js で実装されている<transition>を指します。

transitionの目的は、DOM の変化によるアニメーションを実現することです。 例えばv-ifv-showによる要素の表示・非表示の際のアニメーションを実現します。

まずは簡単な例を見ていただきます。

HTML
<transition>
  <p v-if="isActive">Hello World!</p>
</transition>
JavaScript
data: {
  isActive: true
},
methods: {
  changeActive() {
    this.isActive = !this.isActive
  }
}
CSS
.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 つである必要があります。

OK
<transition>
  <p v-if="isActive">active</p>
  <p v-else>no active</p>
</transition>
NG
<transition>
  <p>active</p>
  <p>no active</p>
</transition>

次にアニメーション用の CSS プロパティを設定します。 transitionでは、要素の状態に応じて以下のクラスが対象の要素に付与されます。

クラス名は、Vue 2 と Vue 3 で一部違う点に注意してください。

クラス(Vue 2)クラス(Vue 3)説明
v-enterv-enter-from表示開始時の状態を設定
v-enter-activev-enter-active表示中のアニメーションを設定
v-enter-tov-enter-to表示終了時の状態を設定
v-leavev-leave-from非表示開始時の状態を設定
v-leave-activev-leave-active非表示中のアニメーションを設定
v-leave-tov-leave-to非表示終了時の状態を設定

各クラスの付与状態は以下のように移り変わります。

タイミングクラスの付与状態
(非)表示開始時v-enter, v-enter-activev-leave, v-leave-active
(非)表示中v-enter-active, v-enter-tov-leave-active, v-leave-to
(非)表示終了時v-enter-active, v-enter-tov-leave-active, v-leave-to
(非)表示終了後

name

transitionにはnameを設定することができます。

HTML
<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-classenter-from-classv-enterv-enter-from)を上書き
enter-active-classenter-active-classv-enter-activeを上書き
enter-to-classenter-to-classv-enter-toを上書き
leave-classleave-from-classv-leavev-leave-from)を上書き
leave-active-classleave-active-classv-leave-activeを上書き
leave-to-classleave-to-classv-leave-toを上書き

複数のクラスを指定する場合はスペースで区切ります。

HTML
<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非表示キャンセル時
HTML
<transition
  @enter="enter"
  @after-enter="afterEnter"
  @leave="leave"
  @after-leave="afterLeave"
>
  <p v-if="isActive">Hello World!</p>
</transition>
<p>{{ status }}</p>
JavaScript
data: {
  isActive: true,
  status: ''
}
methods: {
  enter() {
    this.status = '表示中'
  },
  afterEnter() {
    this.status = ''
  },
  leave() {
    this.status = '非表示中'
  },
  afterLeave() {
    this.status = ''
  }
}

Hello World!


状態:


各メソッドの引数として、対象の要素を受け取ることができ、直接スタイルを変更することもできます。

JavaScript
methods: {
  beforeEnter(el) {
    el.style.opacity = 0
  }
}

仮に CSS を使わず、JavaScript 内ですべてのスタイルを変更する場合は、cssプロパティをfalseに設定します。 これによってパフォーマンスの向上が得られるようです。

HTML
<transition @before-enter="befoerEnter" :css="false">
  <p v-if="isActive">Hello World!</p>
</transition>

appear

appearはページの初回表示時のアニメーション実装で使用します。

HTML
<transition appear>
  <p>Hello World</p>
</transition>
CSS
.v-enter-active {
  transition: all 1s;
}
.v-enter {
  opacity: 0;
}

appearは、appear-classappear-active-classappear-to-classと専用のカスタムトランジッションクラスがあります。

また JavaScript フックについても、before-appearappearafter-appearと専用のものがあります。

duration

要素の表示、非表示までの時間を設定するためのプロパティです。 時間はミリ秒で設定します。

HTML
<transition :duration="1000">
  <p v-if="isActive">Hello World!</p>
</transition>

また表示(enter)と非表示(leave)の時間を別に設定することもできます。

HTML
<transition :duration="{ enter: 1000, leave: 2000 }">
  <p v-if="isActive">Hello World!</p>
</transition>

注意点として、このdurationと CSS で設定しているdurationは別の物です。 この違いは以下のプレビューで体感できると思います。

HTML
<transition :duration="3000">
  <p v-if="isActive">Hello World!</p>
</transition>
CSS
.v-enter-active,
.v-leave-active {
  transition: all 1s;
}

Hello World!


key, mode

v-ifv-elseのように、複数の条件によって要素が切り替わる場合は、その要素を一意に識別するためのkeyの設定が必要になります。 keyの設定がない場合は、アニメーションが機能しません。

HTML
<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 倍の時間がかかります。

HTML
<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によるリスト表示などまとまった要素のアニメーションの実装で使用します。

HTML
<transition-group tag="p">
  <span v-for="n in numbers" :key="n">{{ n }}</span>
</transition-group>
JavaScript
data: {
  numbers: [0, 1, 2]
},
methods: {
  plus() {
    this.numbers.shift()
    this.numbers.push(this.numbers[this.numbers.length - 1] + 1)
  }
}
CSS
.v-enter-active,
.v-leave-active {
  transition: all 1s;
}
.v-enter {
  opacitry: 0;
  transform: translateX(50%);
}
.v-leave-to {
  opacity: 0;
  transform: translateX(-50%);
}
012

tagは、親となる要素を指定することができます。 上の例では以下のように展開されます。

HTML
<p>
  <span>1</span>
  <span>2</span>
  <span>3</span>
</p>

transition-groupでは特別に、v-modeというクラスが付与されます。 これは要素の入れ替わりがあった場合に付与されるクラスになります。

HTML
<transition-group tag="p">
  <span class="number" v-for="n in numbers" :key="n">{{ n }}</span>
</transition-group>
JavaScript
data: {
  numbers: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
},
methods: {
  reverse() {
    this.numbers = this.numbers.reverse()
  }
}
CSS
.v-move {
  transition: transform 1s;
}
012456789

要素の変化はkeyの値によって判断されます。 例えば以下のようにインデックスをkeyに設定します。

HTML
<transition-group tag="p">
  <span class="number" v-for="(n, index) in numbers" :key="index">{{ n }}</span>
</transition-group>

これで同じ処理をしたとしてもアニメーションは実行されません。 なぜなら配列の値が変化したとしても、インデックス自体に変化がないためです。

そもそもインデックスをkeyとすることは非推奨とされています。