Vue.jsで要素を表示したままアニメーション



Vueのアニメーションで実装方法を検索すると、「transition」がよく出てくるかと思います。
この「transition」もとても便利なのですが、
コンポーネントが表示・非表示になる時以外でも要素にアニメーションをつけたかったので、
CSSとVueを使って実装してみました。

今回はよく見かけるカルーセルの一種で、
次の画像がちら見えしてるパターンを作成した方法を紹介したいと思います。
↓こんな感じの！
![sample_animation.png](https://qiita-image-store.s3.amazonaws.com/0/143859/495b3307-7d3b-047e-1f9a-0a9281b22d3e.png)


##transform、transition-durationを使ってこんなに簡単に！

動かしたい要素に対して
translateとtransition-durationを指定することで簡単にアニメーションを実装することが出来ます。

[■こちらのページから動くデモを確認できます](https://codesandbox.io/s/1vlo61vnj3)

```App.vue
<template>
  <div id="app">
    <div class="sample">
      <div class="sample__wrap">
        //スライドさせたい要素のstyleにtransformを指定し、位置が動的に変わるように設定する（${position}pxの部分）
        <ul
          class="sample__list"
          :style="{ transform: `translate(${position}px, 0)` }"
        >
          <li class="sample__item sample__item--pink">1</li>
          <li class="sample__item sample__item--green">2</li>
          <li class="sample__item sample__item--orange">3</li>
          <li class="sample__item sample__item--blue">4</li>
          <li class="sample__item sample__item--yellow">5</li>
          <li class="sample__item sample__item--red">6</li>
        </ul>
      </div>
      <div
        class="sample__btn sample__btn--prev"
        @click="moveContainer('left');"
        :class="{ isDisabled: this.rightEnd }"
      ></div>
      <div
        class="sample__btn sample__btn--next"
        @click="moveContainer('right');"
        :class="{ isDisabled: this.leftEnd }"
      ></div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      itemWidth: 200,
      position: 0,
      leftEnd: true,
      rightEnd: false
    };
  },
  methods: {
    moveContainer(turn) {
      if (turn === "left") {
        if (this.position < -(this.itemWidth * 4)) {
          return;
        }
        this.position = this.position - this.itemWidth;
      } else {
        if (this.position === 0) {
          return;
        }
        this.position = this.position + this.itemWidth;
      }
      this.leftEnd = this.position === 0 ? true : false;
      this.rightEnd = this.position < -(this.itemWidth * 4) ? true : false;
    }
  }
};
</script>

<style lang="scss">
.sample {
  position: relative;
  margin: 100px auto;
  width: 300px;
  &__wrap {
    overflow: hidden;
    border: 5px solid #848484;
  }
  &__list {
    //translateの値が変化した時に、
    //ここのtransition-durationの指定でアニメーションしながら要素が動く
    transition-duration: 0.8s;
    margin: 0;
    padding: 0;
    display: flex;
    width: 1200px;
  }
  &__item {
    list-style: none;
    width: 200px;
    height: 200px;
    line-height: 200px;
    color: #fff;
    font-size: 50px;
    font-weight: bold;
    text-align: center;
    &--pink {
      background: #ff69b4;
    }
    &--green {
      background: #3cb371;
    }
    &--orange {
      background: #ffa500;
    }
    &--blue {
      background: #6495ed;
    }
    &--yellow {
      background: #ffd700;
    }
    &--red {
      background: #ff6347;
    }
  }
  &__btn {
    position: absolute;
    width: 40px;
    height: 40px;
    background-color: rgba(0, 0, 0, 0.5);
    border-radius: 50%;
    &:hover {
      opacity: 0.8;
    }
    &:before {
      position: absolute;
      top: 0;
      bottom: 0;
      left: 0;
      margin: auto;
      content: "";
      vertical-align: middle;
    }
    &--prev {
      bottom: 80px;
      left: -60px;
      &:before {
        left: 13px;
        width: 14px;
        height: 14px;
        border-bottom: 4px solid #fff;
        border-left: 4px solid #fff;
        -webkit-transform: rotate(45deg);
        transform: rotate(45deg);
      }
    }
    &--next {
      bottom: 80px;
      right: -60px;
      &:before {
        left: 9px;
        width: 14px;
        height: 14px;
        border-top: 4px solid #fff;
        border-right: 4px solid #fff;
        -webkit-transform: rotate(45deg);
        transform: rotate(45deg);
      }
    }
  }
}
.isDisabled {
  opacity: 0.2;
  &:hover {
    opacity: 0.2;
  }
}
</style>

```

translateの値を動的にする事でユーザーがクリックしたら、何か表示が変わったら、などなど色んな応用ができます。

##まとめ
Vueのアニメーションで検索すると、「transition」の記事ばかりがでて、やりたいことが出来なかったりしましたが、
CSSを使う事でJQueryで実装していたようなUIも実装できるようになります。
（「transition」で実装できる箇所は「transition」を使うべきですが）
CSSのアニメーションも大分充実してきたので、うまくVue.jsと合わせて使っていきたいですね。