【Vue.js】transition-groupで縦スクロールのカルーセルを実現

## 完成形（実現したいこと）

自動で縦方向にリストが切り替わるカルーセルを作りたい。
cssで頑張らずにVue.jsのtransitionのみで実現させたい。

![ezgif.com-video-to-gif.gif](https://qiita-image-store.s3.amazonaws.com/0/239038/8fa97e27-7038-e738-1d3b-b617cf730674.gif)

## まずはVue.jsでリスト表示

```index.vue
<template>
  <div class="fruit">
    <span v-for="item in items" :key="item.id" class="fruit__list__item">{{ item.title }}</span>
  </div>
</template>

<script>
export default {
  data() {
    return {
      items: [
        { id: 1, title: "apple" },
        { id: 2, title: "banana" },
        { id: 3, title: "orange" },
        { id: 4, title: "peach" },
        { id: 5, title: "plum" },
        { id: 6, title: "grape" }
      ]
    };
  }
};
</script>
```

最終的には３件表示にしますが、とりあえず全件v-forで表示。
<img width="150" alt="スクリーンショット 2019-02-20 0.44.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/239038/91a8d3f1-784c-4d9d-a9e2-9f2b36eefa97.png">

## リストが動くようにVueの処理を追加

```index.vue
<template>
　　<!-- itemsをshowItemに変更。他箇所は略。 -->
　　<span v-for="item in showItem" :key="item.id" class="fruit__list__item">{{ item.title }}</span>
</template>

<script>
export default {
 // （略）
  created() {
    this.scrollUp();
  },
  computed: {
    showItem() {
      return this.items.slice(0, 3);
    }
  },
  methods: {
    scrollUp() {
      window.setInterval(() => {
        this.items = this.items.concat(this.items.shift());
      }, 2000);
    }
  }
};
</script>
```

- `created` インスタンスが生成された後に1度だけ `scrollUp` を実行
- `scrollUp` で配列の0番目を削除し、配列の後ろに連結することで無限スクロールを実現
- `scrollUp` で `setInterval` することで 2秒に1度配列が変更される
- `showItem` 配列の0番目から3番目の3件のみを表示

これで、なめらかな動き以外は、実現したい動きにすることができました。

![ezgif.com-video-to-gif (1).gif](https://qiita-image-store.s3.amazonaws.com/0/239038/f3dee404-40d4-d5dc-0b3f-e25901766f9e.gif)

## transitionでなめらかな動きにする

リストをtransitionさせるときは、 `transition-group` を使います。

```index.vue
<template>
  <div class="fruit">
    <transition-group name="fruit-list" tag="p" class="fruit__list">
      <span v-for="item in showItem" :key="item.id" class="fruit__list__item">{{ item.title }}</span>
    </transition-group>
  </div>
</template>
```

```style.scss
.fruit {
  &__list {
    display: flex;
    flex-direction: column;

    &__item {
      margin: 5px;
      padding: 5px;
      border: 1px solid;
      transition: all 1s;
    }
  }

  &-list-enter {
    opacity: 0;
    transform: translateY(35px);
  }

  &-list-leave-to {
    opacity: 0;
    transform: translateY(-35px);
  }

  &-list-leave-active {
    position: absolute;
  }
}
```

transition部分はほとんど公式と同じなので詳しくはそちらを参照ください。

注意点としては `v-leave-active` に `position: absolute` を指定しないと
先頭の要素が上に移動しても後続がついてこないため、必須となります。

## 注意

```index.vue
<transition-group name="fruit-list" tag="p" class="fruit__list">
    <span v-for="(item, index) in showItem" :key="index" class="fruit__list__item">{{ item.title }}</span>
</transition-group>
```

keyをv-forのindexに指定するとエラーになってしまいます。
（わたしは最初やってしまいました。。）

`  - Do not use v-for index as key on <transition-group> children, this is the same as not using keys.`

indexではなく、itemのidなどで指定が必要です。

## 参考

- [トランジションクラス — Vue.js](https://jp.vuejs.org/v2/guide/transitions.html#%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B8%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%AF%E3%83%A9%E3%82%B9)
- [リスト移動トランジション — Vue.js](https://jp.vuejs.org/v2/guide/transitions.html#%E3%83%AA%E3%82%B9%E3%83%88%E7%A7%BB%E5%8B%95%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B8%E3%82%B7%E3%83%A7%E3%83%B3)