【vue-awesome-swiper】サムネイル付きスライダーの実装

# はじめに
swiperを用いたスライダーの実装の記事はたくさんあったが、
サムネイル画像もスライダーにする実装ばかりでサムネイル画像を固定画像とするものが少なかったので
残しておきます。

# 環境
- MacOS
- yarn 1.22.17
- node v14.17.1
- vue 2.6.14 
- vue-awesome-swiper 4.1.1

# 完成品
以下のようなサムネイル付きスライダーを作成します！
![slicer.gig.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2375107/2cc2ef0b-3d2f-122e-8f40-63b8d059ac48.gif)

# 全体コード

```vuejs
<template>
  <div class="room-swiper">
    <Swiper ref="swiperRef" class="top" :options="swiperOption">
      <SwiperSlide
        v-for="(slider, index) in sliderContents"
        :key="index"
        class="slide"
      >
        <img class="image" :src="slider" />
      </SwiperSlide>
    </Swiper>
    <div class="thumbs">
      <div
        v-for="(thumbs, index) in thumbsContents"
        :key="index"
        class="slide"
        :class="{ '-active': index === activeIndex }"
      >
        <img class="image" :src="thumbs" @click="clickHandler(index)" />
      </div>
    </div>
  </div>
</template>
<script lang="ts">
import { defineComponent, ref } from '@nuxtjs/composition-api';
import SwiperClass from 'swiper';
import { Swiper, SwiperSlide } from 'vue-awesome-swiper';

export default defineComponent({
  components: {
    Swiper,
    SwiperSlide,
  },
  props: {
    sliderContents: {
      type: Array,
      required: true,
    },
  },
  setup() {
    type SwiperModule = typeof Swiper & {
      $swiper: SwiperClass;
      initSwiper: () => void;
    };

    const swiperRef = ref<SwiperModule>();
    const activeIndex = ref<number>(0);

    // 上のスワイパーは画面読み込み時2番目が表示されているため下のサムネイルも初期選択が2番目になるようにしている
    const thumbsContents = [
      ...props.sliderContents.slice(1),
      props.sliderContents[0],
    ];

    const swiperOption = {
      loop: true,
      loopedSlides: 3,
      spaceBetween: 10,
      slidesPerView: 3,
      autoplay: {
        delay: 5000,
        disableOnInteraction: false,
      },
      on: {
        slideChange: () => {
          if (!swiperRef.value || !swiperRef.value.$swiper) return;
          activeIndex.value = swiperRef.value.$swiper.realIndex;
        },
      },
    };

    const clickHandler = (index: number) => {
      if (
        !swiperRef.value ||
        !swiperRef.value.$swiper ||
        !swiperRef.value.$swiper.controller
      )
        return;
      swiperRef.value?.$swiper.slideToLoop(index);
    };

    return {
      swiperRef,
      activeIndex,
      swiperOptionTop,
      clickHandler,
      thumbsContents,
    };
  },
});
</script>
```

# 解説
## スワイパー部分
propsで受け取ったsliderContents(画像の配列)をv-forで回しています。
また、optionを `swiperOption`として渡しています。
`Swiper`、`SwiperSlide` の使い方に関しては以下を参考にしてください。
[https://github.com/surmon-china/vue-awesome-swiper](https://github.com/surmon-china/vue-awesome-swiper)

```vuejs
<Swiper ref="swiperTop" class="top" :options="swiperOptionTop">
      <SwiperSlide
        v-for="(slider, index) in sliderContents"
        :key="index"
        class="slide"
      >
        <img class="image" :src="slider" />
      </SwiperSlide>
</Swiper>
```
##オプション
[オプション一覧](https://coder-memo.com/swiper-option/)

```javascript
const swiperTop = ref<SwiperModule>();
const activeIndex = ref<number>(0);

const swiperOption = {
      loop: true,
      loopedSlides: 3,
      spaceBetween: 10,
      slidesPerView: 3,
      autoplay: {
        delay: 5000,
        disableOnInteraction: false,
      },
      on: {
        slideChange: () => {
          if (!swiperTop.value || !swiperTop.value.$swiper) return;
          activeIndex.value = swiperTop.value.$swiper.realIndex;
        },
      },
    };
```

`slideChange`オプションを指定することで、画像がスライドする時に発火してくれます。
`realIndex`にはスライドしている画像のインデックス番号が入ります。

```typescript
on: {
    slideChange: () => {
      if (!swiperTop.value || !swiperTop.value.$swiper) return;
      activeIndex.value = swiperTop.value.$swiper.realIndex;
     },
},
```

## サムネイル
画像クリック時に`clickHandler`を発火させています。
今回はcssを省いていますが、v-forで回しているindex番号とactiveIndexが一致した時にクラスをつけるというイメージです。
そのクラスに`opacity`を指定すれば、それっぽくなると思います！

```vuejs

 <div class="thumbs">
      <div
        v-for="(thumbs, index) in thumbsContents"
        :key="index"
        class="slide"
        :class="{ '-active': index === activeIndex }"
      >
        <img class="image" :src="thumbs" @click="clickHandler(index)" />
      </div>
    </div>
```

```javascript
const clickHandler = (index: number) => {
      if (
        !swiperTop.value ||
        !swiperTop.value.$swiper ||
        !swiperTop.value.$swiper.controller
      )
        return;
       // 引数に指定したスライドに移動させる slideTo というメソッドを使って移動させる。loopの設定をしている場合はslideToLoop を使う
      swiperTop.value?.$swiper.slideToLoop(index);
    };
```

# さいごに
サムネイル画像付きのスライダーはなんかかっこいいですね！！！