はじめに
jQueryのSlideJSなどでクロスフェードなスライドカルーセルを実現したことはあったのですが、それをVue.jsでもやってみました。
なお、以下の通りです。
- Vue.jsの使い方には言及しません。
- transitionの詳しい使い方は、https://jp.vuejs.org/v2/guide/transitions.html を参照ください。
概要
- Vueでコンポーネント化しています。
- Vue.jsのtransitionタグを使います。
- Vueの環境はvue-cli3を使って作っています。
環境的なもの
- vue --version 3.5.1
- node --version v10.8.0
- VueをTypeScriptで記述
- HTMLはpugで記述
- cssはstylusで記述
全体像
何はともあれ、まず全体像。
sample.vue
<template lang="pug">
div
.slide-outer
transition(name="fade")
.slide-inner(v-for="(slide, idx) in slides" v-if="currentSlide == idx" :key="idx")
img.slide-img(:src="slides[idx].img")
</template>
<script lang="ts">
import { Component, Watch, Vue } from 'vue-property-decorator'
@Component({
})
export default class SlideShow extends Vue {
private currentSlide: number = 0
private slides: any = [
{ img: require('@/assets/home_slide_1.jpg') },
{ img: require('@/assets/home_slide_2.jpg') },
{ img: require('@/assets/home_slide_3.jpg') },
{ img: require('@/assets/home_slide_4.jpg') }
]
private fade: string = 'next'
private show: boolean = true
private timer: number = 0
created () {
this.$nextTick(() => {
this.timer = setInterval(() => {
this.autoPlay()
}, 3000)
})
}
autoPlay () {
this.currentSlide++
if (this.currentSlide === this.slides.length) {
this.currentSlide = 0
}
}
}
</script>
<style lang="stylus">
.slide-outer
position relative
overflow hidden
width 100vw
height 300px
display flex
.slider-inner
position absolute
width 100vw
height 300px
.slide-img
width 100vw
height 300px
object-fit cover
.fade-enter-active
transition all 1s ease
.fade-leave-active
transition all 1s ease
position absolute
.fade-enter
.fade-leave-to
opacity 0
</style>
HTML部分(<template>タグ内)
<template lang="pug">
div
.slide-outer
transition(name="fade")
.slide-inner(v-for="(slide, idx) in slides" v-if="currentSlide == idx" :key="idx")
img.slide-img(:src="slides[idx].img")
</template>
- 後で<script>内を見ればわかりますが、
private slides: any = [
{ img: require('@/assets/home_slide_1.jpg') },
{ img: require('@/assets/home_slide_2.jpg') },
{ img: require('@/assets/home_slide_3.jpg') },
{ img: require('@/assets/home_slide_4.jpg') }
]
という形でスライドショーにしたい画像のリストを作っています。そして、v-for
を使って回しています。
- 画像自体は、assetsディレクトリ配下に配置し、
home_slide_[idx].jpg
という名前にしています。 -
v-for
で回し、要素のインデックス番号が、imgタグ
でv-bind
してあるsrc
部分の[idx]
内に順々に入っていき、画像が入れ替わっていくようになリます。 -
.slide_inner
にv-if
を使い、currentSlideとインデックスが同じ番号の画像を表示させるようにしています。
<script>部分
<script lang="ts">
import { Component, Watch, Vue } from 'vue-property-decorator'
@Component({
})
export default class SlideShow extends Vue {
private currentSlide: number = 0
private slides: any = [
{ img: require('@/assets/home_slide_1.jpg') },
{ img: require('@/assets/home_slide_2.jpg') },
{ img: require('@/assets/home_slide_3.jpg') },
{ img: require('@/assets/home_slide_4.jpg') }
]
private fade: string = 'next'
private show: boolean = true
private timer: number = 0
created () {
this.$nextTick(() => {
this.timer = setInterval(() => {
this.autoPlay()
}, 3000)
})
}
autoPlay () {
this.currentSlide++
if (this.currentSlide === this.slides.length) {
this.currentSlide = 0
}
}
}
</script>
- createdした時に
setInterval
でautoPlay()
が発火するようにしています。 -
autoPlay()
のメソッド内で、currentSlide
の値をインクリメントしていき、slides
のリストの長さと同じになった時、インデックスの値を0
にするようにしています。
<style>内(css)
<style lang="stylus">
.slide-outer
position relative
overflow hidden
width 100vw
height 300px
display flex
.slider-inner
position absolute
width 100vw
height 300px
.slide-img
width 100vw
height 300px
object-fit cover
.fade-enter-active
transition all 1s ease
.fade-leave-active
transition all 1s ease
position absolute
.fade-enter
.fade-leave-to
opacity 0
</style>
- ブラウザ幅いっぱいの画像にしたかったため、widthには
vw
を使っています。画像の幅や大きさは好みに合わせて調整してください。 - Vue.jsで
<transition>タグ
を使うと、[transitionタグのname属性で指定した名前]-enter
や[transitionタグのname属性で指定した名前]-leave
などがクラス名で使えるようになります。○○-enter-active
や、○○-leave-active
で、transitionの動きを決めれるようになります。 -
.fade-leave-active
にpositinon absolute
をしておかないと、クロスフェードがうまく効きません。フェードアウトはうまくいってもフェードインが「パチっ」と画像は挿入される感じなってしまいます(ここで若干ハマりました...)。
デモ
ということで、デモです。
大きさ(横幅など)はコンバートしているので、小さくなってます。
こんな感じで動くというのがわかればと思います。
その他補足
- アニメーションの切り替わりの部分は、cssのtransitionプロパティの時間をうまく調整してみてください。
- 画像自体の入れ替えまでの時間は、scriptのsetTimeoutの時間で調整してみてください。
以上。
※ 参考にさせていただいた記事
https://qiita.com/ktn/items/65a0e3112246f86d04de