5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Vue.jsのtransitionでクロスフェードなスライドショーを実現する。

Last updated at Posted at 2019-04-21

はじめに

jQueryのSlideJSなどでクロスフェードなスライドカルーセルを実現したことはあったのですが、それをVue.jsでもやってみました。

なお、以下の通りです。

概要

  • 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_innerv-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した時にsetIntervalautoPlay()が発火するようにしています。
  • 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-activepositinon absoluteをしておかないと、クロスフェードがうまく効きません。フェードアウトはうまくいってもフェードインが「パチっ」と画像は挿入される感じなってしまいます(ここで若干ハマりました...)。

デモ

ということで、デモです。
大きさ(横幅など)はコンバートしているので、小さくなってます。
こんな感じで動くというのがわかればと思います。
ezgif.com-video-to-gif.gif

その他補足

  • アニメーションの切り替わりの部分は、cssのtransitionプロパティの時間をうまく調整してみてください。
  • 画像自体の入れ替えまでの時間は、scriptのsetTimeoutの時間で調整してみてください。

以上。

※ 参考にさせていただいた記事
https://qiita.com/ktn/items/65a0e3112246f86d04de

5
5
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?