LoginSignup
18
16

More than 3 years have passed since last update.

Vue.jsで画像をtransitionさせてはいけない

Last updated at Posted at 2019-07-02

Vue.jsのtransitionを使うと、フェードイン・フェードアウトを伴うスライドショーを簡単に作れます。

<div id="app">
  <transition name="fade">
    <img :src="images[index]" :key="images[index]" width="320" height="213" class="slideshow">
  </transition>
</div>
new Vue({
  el: "#app",
  data: {
    index: 0,
    images: [
      'https://upload.wikimedia.org/wikipedia/commons/thumb/9/9c/Lagopus_muta_japonica_Mount_Tsubakuro.jpg/320px-Lagopus_muta_japonica_Mount_Tsubakuro.jpg',
      'https://upload.wikimedia.org/wikipedia/commons/thumb/4/43/Pair_of_mandarin_ducks.jpg/320px-Pair_of_mandarin_ducks.jpg',
      'https://upload.wikimedia.org/wikipedia/commons/thumb/7/7c/Vicu%C3%B1a_-_Chimborazo%2C_Ecuador.jpg/320px-Vicu%C3%B1a_-_Chimborazo%2C_Ecuador.jpg',
    ]
  },
  mounted() {
    setInterval(() => {
        this.index = this.index < this.images.length - 1 ? this.index + 1 : 0;
    }, 3000);
  },
})
.fade-enter-active, .fade-leave-active {
  transition: opacity 0.5s;
}
.fade-enter, .fade-leave-to {
  opacity: 0;
}
.slideshow {
  position: absolute;
}

このコードは https://jsfiddle.net/76xkzqmg/1/ で動作確認できます。

img要素のsrcを動的に書き換えて、transitionさせているので、一見シンプルで良い実装に見えます。しかし、このコードには大きな問題があります!

ChromeのDevToolsを開いて、Networkタブを見てみましょう。すると、画像が切り替わるたびに、毎回画像がダウンロードされていることがわかります。。

image.png

もし、スライドショーに使う画像が1枚1MBで、5秒ごとに切り替わるとすると、1分で12MB、1時間で720MBをダウンロードすることになります。

また、このダウンロードは、PC版のChromeの場合、タブがアクティブでないときでも発生します。従量課金制の回線を使っている際に、そんなページを開きっぱなしにしていたら、、、:scream:

ということで、無限ダウンロードが発生しないよう、対策を考えます。

transitionの仕組みとimg要素

Vue.jsのtransitionは、要素の追加・削除にフックしてアニメーションを行います。そのため、単にsrc属性を書き換えるだけでなく、img要素自体をDOMから消して、新しく追加しなければなりません。このために、サンプルコードでは key 属性を利用しています。

一方、img要素は新しく追加されると、src属性に指定されたURLに画像を取りに行きます。

この2つの機能が組み合わさることで、無限ダウンロードが発生しています。

transitionを使わず、実装する方針を考えた方がよさそうです。

CSSアニメーションによるスライドショー

Vueのtransitionを使わなくても、JavaScriptとCSSアニメーションの組み合わせで、スライドショーは実現できます。

<div id="app">
  <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/9c/Lagopus_muta_japonica_Mount_Tsubakuro.jpg/320px-Lagopus_muta_japonica_Mount_Tsubakuro.jpg" class="slideshow">
  <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/4/43/Pair_of_mandarin_ducks.jpg/320px-Pair_of_mandarin_ducks.jpg" class="slideshow fadeout">
  <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/7/7c/Vicu%C3%B1a_-_Chimborazo%2C_Ecuador.jpg/320px-Vicu%C3%B1a_-_Chimborazo%2C_Ecuador.jpg" class="slideshow fadeout">
</div>
new Vue({
  el: "#app",
  data: {
    index: 0,
  },
  mounted() {
    const images = document.getElementsByClassName('slideshow');
    this.slideshow(images);
    setInterval(() => {
      this.index = this.index < images.length - 1 ? this.index + 1 : 0;
      this.slideshow(images);
    }, 3000);
  },
  methods: {
    slideshow(images) {
      const current = images[this.index];
      const prev = images[this.index - 1] ? images[this.index - 1] : images[images.length - 1];
      current.classList.add('fadein');
      current.classList.remove('fadeout');
      prev.classList.remove('fadein');
      prev.classList.add('fadeout');
    }
  }
})
.fadein {
  opacity: 1;
  transition: opacity 0.5s;
}

.fadeout {
  opacity: 0;
  transition: opacity 0.5s;
}

.slideshow {
  position: absolute;
}

動作確認は https://jsfiddle.net/tjn4h6yz/ で。

slideshowメソッドで、新しく表示する要素(current)にfadeinクラスを付け、今まで表示していた要素(prev)にはfadeoutクラスを付けています。これによって、Vue.jsのtransitionと同様にアニメーションを付けることができます。

また、この実装の場合、img要素の追加や削除は行っていないので、無限に画像がダウンロードされるような不都合は発生しません。

18
16
0

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
18
16