How to fade in images when loaded with Vue

Viewed 21014

I created this component that fades in an image once it is loaded to the client. I would think there is a more Vue-like way to solve this, like using Vue events, but could not find it. What is the Vue way to detect when an image is loaded?

https://codepen.io/kslstn/pen/ooaPGW

Vue.component('imageThatFadesInOnLoad',{

  data: function(){
    return {
      src: 'http://via.placeholder.com/350x150',
      loaded: false,
    }
  },

  mounted: function () {
    var image = new Image()
    var that =  this
    this.loaded = image.addEventListener('load', function(){that.onLoaded()}) // This is the key part: it is basically vanilla JS
    image.src = this.src
  },

  methods:{
    onLoaded(){
      this.loaded = true
    }
  },

  template: `
    <div class="wrapper">
      <transition name="fade">
        <img class="icon" v-bind:src="src" v-if="loaded">&nbsp;
      </transition>
   </div>
  `
})

new Vue({
  el: '#wrapper'
});
.wrapper{
  width: 350px;
  height: 150px;
  background: slategrey;
}
.fade-enter-active {
  transition: opacity 3s ease-in-out;
}
.fade-enter-to{
  opacity: 1;
}
.fade-enter{
  opacity: 0;
}
<script src="https://unpkg.com/vue@2.4.4/dist/vue.js"></script>
<div id="wrapper">
<image-that-fades-in-on-load></image-that-fades-in-on-load>
</div>

3 Answers

Just to provide a slightly different approach to the answers above, using a native CSS solution instead of the Vue <Transition> way.

In the code below, we just add a class to the <img> when it loads triggering a transition.

<template>
  <img
    :class="imgIsLoaded ? 'show' : ''"
    src="myImg.jpg"
    loading="lazy"
    @load="imgLoadedMethod"
  >
</template>
<script>
export default {
  data () {
    return {
      imgIsLoaded: false
    }
  },
  methods: {
    imgLoadedMethod () {
      this.imgIsLoaded = true
    }
  }
}
</script>
<style scoped>
img {
  opacity: 0;
  transition: 3s;
}

img.show {
  opacity: 1;
}
</style>

To make transitions work when you dynamically replace cached images, you need to add a unique key. Otherwise vue's compiler will only replace the content of the element for efficiency and the transition will not be triggered.

<template>
  <div :style="{width: width+'px', height: height+'px'}">
    <transition name="fade">
      <img
        v-show="loaded"
        @load="onImageLoad"
        :src="src"
        :alt="alt"
        :width="width"
        :height="height"
        :key="src"
      />
    </transition>
  </div>
</template>
<script>
export default {
  props: ["src", "width", "height", "alt"],
  data() {
    return {
      loaded: false,
    };
  },
  methods: {
    onImageLoad() {
      this.loaded = true;
    },
  },
  watch: {
    src: {
      handler() {
        this.loaded = false;
      },
      immediate: true,
    },
  },
};
</script>
<style scoped>
.fade-enter-active {
  transition: opacity 1s ease-in-out;
}
.fade-enter-to {
  opacity: 1;
}
.fade-enter {
  opacity: 0;
}
</style>
Related