Nuxt.js + Swiper スライダーの作り方



Webサイトなどでよく見掛ける、一定時間が経過すると画像が切り替わるスライダーを実装してみました。
前回作成した Nuxt.js プロジェクト に追加したかったので、Swiper の Vue 用プラグインvue-awesome-swiperを使ってスライダーを実装しました。



*スライダーとは

Webサイトのスライドショーのことで、関連するコンテンツを表示したりポートフォリオを展示する場合に使われます。スライダーを使うことで、1つのスクリーン内に複数のコンテンツを表示させることができるようになります。また、ユーザーの興味を引き、ユーザー自身でコンテンツをスキップしたり操作することもできます。


*Swiper とは

簡単にスライダーを作ることができる、JavaScript のライブラリです。機能が多くカスタマイズも柔軟にでき、レスポンシブ表示にも対応しているので大変使いやすいです。こちら のサイトのようなスライダーを簡単に実装することができます。


*参考



*環境

  • macOS
  • nuxt 2.8.1
  • jquery 3.4.1
  • vue-awesome-swiper 3.1.3
  • CSS3


*インストール

プロジェクト配下で下記コマンドを実行してインストールします。
$ npm install vue-awesome-swiper

pluginsディレクトリにvue-awesome-swiper.jsを新規作成します。
<plugins/vue-awesome-swiper.js>
import Vue from 'vue'
import VueAwesomeSwiper from 'vue-awesome-swiper'

Vue.use(VueAwesomeSwiper)

nuxt.config.jsに下記の設定を追加します。
  • css ---- 'swiper/dist/css/swiper.css'
  • plugins ---- { src: '~/plugins/vue-awesome-swiper', ssr: false }
  • build ---- vendor: ['vue-awesome-swiper']
<nuxt.config.js>
...

  /*
   ** Global CSS
   */
  css: ['reset-css', '@/assets/scss/style.scss', 'swiper/dist/css/swiper.css'],
  /*
   ** Plugins to load before mounting the App
   */
  plugins: [
    { src: '~/plugins/vue-scrollmagic.js', ssr: false },
    { src: '~/plugins/vue-awesome-swiper', ssr: false }
  ],

...

  /*
   ** Build configuration
   */
  build: {
    postcss: {
      preset: {
        features: {
          customProperties: false
        }
      }
    },
    vendor: ['vue-awesome-swiper'],
    /*
     ** You can extend webpack config here
     */
    extend(config, ctx) {}
  },

...


*スライダーの実装

Swiper の公式サイト をそのまま記述するだけで、スライダーを実装することができます。Vue だと少しだけ書き方が変わりますが、オプションの定義などは同様です。

基本的な実装方法は下記になります。
  1. <swiper />でスライダーにしたい全体を定義する。
  2. <swiper-slide />でスライダーの中の1つずつの画像を定義する。
  3. data プロパティでswiperOptionを定義する。(スライド数や自動再生の有無など)
  4. swiperOptionを最初に作成した<swiper />に追加する。(:options="swiperOption"
<pages/index.vue>
<template>
  <div>
    <swiper :options="swiperOption">
      <swiper-slide>
     <div>
       <p>技術ブログ</p>
       <a href="https://mmtomitomimm.blogspot.com/" target="_blank">
         <img src="~/assets/images/blog.png" />
       </a>
     </div>
      </swiper-slide>
      <swiper-slide>
     <div>
       <p>ユーザー管理アプリ</p>
       <a :href="https://user-setting-app-20190502.herokuapp.com/" target="_blank">
         <img src="~/assets/images/manage-app.png" />
       </a>
     </div>
      </swiper-slide>
      <swiper-slide>
     <div>
       <p>ブログアプリ</p>
       <a :href="https://calm-beach-62772.herokuapp.com/" target="_blank">
         <img src="~/assets/images/blog-app.png" />
       </a>
     </div>
      </swiper-slide>
    </swiper>
  </div>
</template>
<script>
export default {
  data() {
    return {
      swiperOption: {
        loop: true,
        slidesPerView: 3,
        spaceBetween: 30,
        autoplay: {
          delay: 5000
        }
      }
    }
  },
...
}
</script>

今回追加したオプションについて補足の説明です。
  • loop ---- 自動再生の有無。
  • slidesPerView ---- 表示するスライド数。数値または auto。デフォルトは1
  • spaceBetween ---- スライドの間隔。デフォルトは0
  • autoplay ---- 自動でスライド遷移させるかの設定有無。設定を入れないと自動再生されない。true/falseでの指定も可能ですが、delayで何ミリ秒毎に再生するかの設定ができる。


*前後スライドのナビゲーション

クリックすると前後のスライドへ遷移できるようナビゲーションを追加します。先ほど作成した<swiper />のなかの<swiper-slide />下に、div 要素でbutton-prev/button-nextを追加します。また、data プロパティのswiperOptionnavigationを追加する必要があります。

矢印のスタイルはデフォルトのままだと大サイズの青色になってしまいますが、.swiper-button-prev/.swiper-button-nextのクラスを定義し直すことで変更することができます。
矢印の色はbackground-imageで SVG の定義をしているので、fill%3D’%23007affの後ろ007affの色コードを変更すると反映されるようになっています。
矢印のサイズを変更したい場合はbackground-sizeを指定します。
<pages/index.vue>
<template>
  <div>
    <swiper :options="swiperOption">
      <swiper-slide> ... </swiper-slide>
      <swiper-slide> ... </swiper-slide>
      <swiper-slide> ... </swiper-slide>
      <!---- ↓追加 ---->
   <div slot="button-prev" class="swiper-button-prev"></div>
   <div slot="button-next" class="swiper-button-next"></div>
    </swiper>
  </div>
</template>
<script>
export default {
  data() {
    return {
      swiperOption: {
        loop: true,
        slidesPerView: 3,
        spaceBetween: 30,
        autoplay: {
          delay: 5000
        },
        // ---- ↓追加 ----
        navigation: {
          nextEl: '.swiper-button-next',
          prevEl: '.swiper-button-prev'
        }
      }
    }
  },
...
}
</script>
<style  scoped  lang="scss">
.swiper-button-prev {
  background-size: 15px 15px;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M0%2C22L22%2C0l2.1%2C2.1L4.2%2C22l19.9%2C19.9L22%2C44L0%2C22L0%2C22L0%2C22z'%20fill%3D'%23C0C0C0'%2F%3E%3C%2Fsvg%3E");
}

.swiper-button-next {
  background-size: 15px 15px;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M27%2C22L27%2C22L5%2C44l-2.1-2.1L22.8%2C22L2.9%2C2.1L5%2C0L27%2C22L27%2C22z'%20fill%3D'%23C0C0C0'%2F%3E%3C%2Fsvg%3E");
}
</style>


*レスポンシブ対応

画面サイズごとに表示するスライド数や、スペース間隔を設定することができます。swiperOptionbreakpointsを追加し、画面サイズ毎に定義します。
今回は画面サイズが 1430 px 以下になると表示するスライド数が2つ、950 px 以下になると1tになるようにしています。
<pages/index.vue>
...

  data() {
    return {
      parentRefs: null,
      swiperOption: {
        loop: true,
        slidesPerView: 3,
        spaceBetween: 30,
        autoplay: {
          delay: 5000
        },
        pagenation: {
          el: '.swiper-pagination',
          clickable: true
        },
        navigation: {
          nextEl: '.swiper-button-next',
          prevEl: '.swiper-button-prev'
        },
        // ---- ↓追加 ----
        breakpoints: {
          1430: {
            slidesPerView: 2,
            spaceBetween: 10
          },
          950: {
            slidesPerView: 1
          }
        }
      }
    }
  },
  
...


*ホバーで自動再生を停止/開始

選択したいスライドが決まった場合にスライドが自動で動き続けていると選択しづらいため、スライドをホバーすると停止してホバーをはずすと再度自動で動くようにします。そのためには Swiper で用意されている autoplay.stop()autoplay.start()を使います。(参考: Swiper API / 公式サイト

今回、私が作成した Nuxt.js プロジェクトは細かくコンポーネント化しており、Swiper を使っている親コンポーネントでホバーの判定ができないため、Swiper を子コンポーネントに渡して制御するようにしました。

<swiper />ref="mySwiper"を追加します。refを追加することで、javascript で$refsを使って要素を取得することができます。取得した要素を data プロパティのparentRefsに格納して、子コンポーネントのArticleContent.vuepropsとして渡します。
<pages/index.vue>
<template>
  <div class="page-index">
      <section>
        <h2 id="work-trigger" class="section-title effect-delay-fade-side">
          Works
        </h2>
        <article-body class="works">
          <swiper ref="mySwiper" :options="swiperOption">
            <swiper-slide>
              <article-content
                id="work-image"
                name="技術ブログ"
                url="https://mmtomitomimm.blogspot.com/"
                file="blog.png"
                :parent-refs="parentRefs"
              >
              </article-content>
            </swiper-slide>
            <swiper-slide>
              <article-content
                id="work-image"
                name="ユーザー管理アプリ"
                url="https://user-setting-app-20190502.herokuapp.com/"
                file="manage-app.png"
                :parent-refs="parentRefs"
              >
              </article-content>
            </swiper-slide>
            <swiper-slide>
              <article-content
                id="work-image"
                name="ブログアプリ"
                url="https://calm-beach-62772.herokuapp.com/"
                file="blog-app.png"
                :parent-refs="parentRefs"
              >
              </article-content>
            </swiper-slide>
            <div slot="button-prev" class="swiper-button-prev"></div>
            <div slot="button-next" class="swiper-button-next"></div>
            <div slot="pagination" class="swiper-pagination"></div>
          </swiper>
        </article-body>
      </section>
    </div>
  </div>
</template>
<script>
import ArticleBody from '~/components/common/ArticleBody'
import ArticleContent from '~/components/common/ArticleContent'
export default {
  components: {
    ArticleBody,
    ArticleContent
  },
  data() {
    return {
      parentRefs: null,      // ←追加
      swiperOption: {
        loop: true,
        slidesPerView: 3,
        spaceBetween: 30,
        autoplay: {
          delay: 5000
        },
        pagenation: {
          el: '.swiper-pagination',
          clickable: true
        },
        navigation: {
          nextEl: '.swiper-button-next',
          prevEl: '.swiper-button-prev'
        },
        breakpoints: {
          1430: {
            slidesPerView: 2,
            spaceBetween: 10
          },
          950: {
            slidesPerView: 1
          }
        }
      }
    }
  },
  mounted() {
    // ---- ↓追加 ----
    this.parentRefs = this.$refs.mySwiper.swiper
 ...
  }
}
</script>
<style scoped lang="scss">
...
</style>

子コンポーネントでparentRefsの値を受け取り、autoplay.stop()autoplay.start()の処理を行うようにします。ホバー時のイベントには Vue.js で用意されているイベント修飾子を使うと簡単に実装できます。ホバーしたときのイベントは@mouseover、離れたときは@mouseleaveを使い、それぞれメソッドを呼び出して Swiper API を実行します。
<components/common/ArticleContent.vue>
<template>
  <div class="article-content">
    <div>
      <p>{{ name }}</p>
      <a :href="url" target="_blank">
        <img
          :src="getImage()"
          @mouseover="stopImage()"
          @mouseleave="startImage()"
        />
      </a>
    </div>
  </div>
</template>
<script>
export default {
  props: {
    name: {
      required: true,
      type: String
    },
    url: {
      required: true,
      type: String
    },
    file: {
      required: true,
      type: String
    },
    // ---- ↓追加 ----
    parentRefs: {
      type: Object,
      required: false,
      default: null
    }
  },
  methods: {
    getImage() {
      return require(`~/assets/images/${this.file}`)
    },
    // ---- ↓追加 ----
    stopImage() {
      this.parentRefs.autoplay.stop()
    },
    // ---- ↓追加 ----
    startImage() {
      this.parentRefs.autoplay.start()
    }
  }
}
</script>
<style scoped lang="scss">
...
</style>


*所感

今回使った Swiper 以外にも Vue-slick というプラグインがあって使ってみたのですが、ボタンのカスタマイズなどスタイルの変更がうまくいきませんでした。(私の使い方が悪かったのかもしれませんが、参考になるドキュメントが少なく、時間がかかったので諦めました)
それに比べて Swiper は参考になるドキュメントも多く、スタイルも柔軟に変更できたので使いやすかったです。ページングの表示もできるようなので、時間のあるときに試してみたいと思います。


Previous
Next Post »

1 コメント:

Write コメント
匿名
AUTHOR
2022年1月28日 7:10 delete

How do you make money from gambling? - Work Tomake Money
It's also one of หารายได้เสริม the simplest of all types of septcasino betting and kadangpintar A win on a particular game can mean a particular outcome, and the outcome of that

Reply
avatar

人気の投稿