Search code examples
cssvue.jsbackground-image

add transition effect in vue on backgroundImage


I've been searching for a couple of days to add a fade transition effect on the backgroundImage that I'm changing through a Vue app.

Here is the code snippet I've been testing on:

new Vue({
  el: "#main",
  data: {
    images: [
      "https://images.freeimages.com/images/large-previews/bfd/clouds-1371838.jpg",
      "https://images.freeimages.com/images/large-previews/ffa/water-lilly-1368676.jpg",
      "https://images.freeimages.com/images/large-previews/efb/lotus-flower-1382251.jpg"
    ],
    current: 0,
    show: false
  },
  methods: {
    changeBG: function () {
      if (this.current < this.images.length - 1) {
        this.current = this.current + 1;
      } else {
        this.current = 0;
      }
    }
  }
});
.main {
  height: 800px;
  width: 100%;
  margin-left: auto;
  margin-right: auto;
  z-index: 0;
  background-repeat: no-repeat;
  background-size: cover;   
  background-position: center 0px; 
} 
button {
  width: 200px;
  height: 25px;
  margin: 10px; 
}
p.hello{
  color: white;
  margin: 10px;
  font-size: 50px;
}

.fade-enter-active,
.fade-leave-active {
  transition: all 2s linear;
}

.fade-enter-to,
.fade-leave {
  opacity: 0;
}

.fade-enter,
.fade-leave-to {
  opacity: 1;
}

/* hello example transition */
.slide-fade-enter-active {
  transition: all 1s ease;
}
.slide-fade-leave-active {
  transition: all 1s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter,
.slide-fade-leave-to {
  transform: translateX(10px);
  opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<transition name="fade">
  <div v-if="changeBG" class="main" id="main" :style="{ backgroundImage: 'url(\'' + images[current] + '\')' }">
    <button v-on:click="changeBG">
      changeBG
    </button>
    <div id="testFromGuide">
      <button @click="show = !show">
        toggleHello
      </button>
      <transition name="slide-fade">
        <p class="hello" v-if="show">all your base are belong to us</p>
      </transition>
    </div>
  </div>
</transition>

My first question if this is simply possible? The reason I'm using backgroundImage is because the website I'm using this on has a background that is most easy to handle responsively through this (always covering, no repeat and keeping it center). And my scond question would be, if not, is there a possibility to make it work with a background set as described here?

In the codepen I've added an example of the vue guide to make sure it works and nothing is else is wrong. And the example works perfectly. Can't seem to find the answer for my example but I've been beginning to suspect it is simply not possible or I can't seem to find why vue isn't detecting something is changing.


Solution

  • For Vue Transitions to work, you need to change the DOM elements. So this way would work if you were changing actual images out. In your example, you're only changing an attribute value. The DOM doesn't trigger a transition since its the same element.

    However, you can use the :key attribute to convince VUE to replace the element thus giving you a transition between 2 elements.

    You can also set the image with inline CSS like you're doing in the example. You'll still have to create the transition in your CSS.

    Here's an example using Vue Transition

    new Vue({
      el: "#main",
      data: {
    		currentID: 0,
        images: [
    'https://images.freeimages.com/images/large-previews/efb/lotus-flower-1382251.jpg',
    'https://images.freeimages.com/images/large-previews/ffa/water-lilly-1368676.jpg',
    'https://images.freeimages.com/images/large-previews/bfd/clouds-1371838.jpg'
    		]
      },
      methods: {
        toggleImage: function(){
           if(this.currentID < this.images.length-1){
    				 this.currentID +=1
    			 } else {
    				 this.currentID = 0
    			 } 	   
        }
      }
    });
    body {
      overflow: hidden;
    }
    
    .main {
      position: relative;
    }
    
    img {
      width: 100%;
      height: auto;
      display: block;
      position: absolute;
      -webkit-transition: all 3s ease;
      transition: all 3s ease;
    }
    
    button {
      z-index: 100;
      position: relative;
      width: 200px;
      height: 25px;
      margin: 20px;
    }
    
    /* prefix with transition name */
    .slide-fade-enter-active {
      opacity: 1;
      z-index: 10;
    }
    
    .slide-fade-leave-active {
      opacity: 1;
    }
    
    .slide-fade-enter,
    .slide-fade-leave-to {
      opacity: 0;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js"></script>
    <div class="main" id="main">
        <transition name="slide-fade">
    <!-- 			SRC comes from the array of images the :key is important for vue to believe its a 'new' DOM element and do the transition -->
          <img v-bind:src="images[currentID]" v-bind:key="currentID" />
        </transition>
    	<button @click="toggleImage">
          Toggle Image
        </button>
    </div>

    However, you don't get a lot of control over that since it uses image tags. Instead, it might be better to use a background image like this:

    new Vue({
      el: "#main",
      data: {
    		currentID: 0,
        images: [
    'https://images.freeimages.com/images/large-previews/efb/lotus-flower-1382251.jpg',
    'https://images.freeimages.com/images/large-previews/ffa/water-lilly-1368676.jpg',
    'https://images.freeimages.com/images/large-previews/bfd/clouds-1371838.jpg'
    		]
      },
      methods: {
        toggleImage: function(){
           if(this.currentID < this.images.length-1){
    				 this.currentID +=1
    			 } else {
    				 this.currentID = 0
    			 } 	   
        }
      }
    });
    .main {
      /* make this the size of the window */
      position: absolute;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
    }
    
    .theImage {
      width: 100%;
      height: 100%;
      position: absolute;
      background-color: #333;
      background-size: contain;
      background-repeat: no-repeat;
      background-position: center center;
      -webkit-transition: all 3s ease;
      transition: all 3s ease;
    }
    
    button {
      z-index: 100;
      position: relative;
      width: 200px;
      height: 25px;
      margin: 20px;
    }
    
    /* prefix with transition name */
    .slide-fade-enter-active {
      opacity: 1;
      z-index: 10;
    }
    
    .slide-fade-leave-active {
      opacity: 1;
    }
    
    .slide-fade-enter,
    .slide-fade-leave-to {
      opacity: 0;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js"></script>
    <div class="main" id="main">
        <transition name="slide-fade">
    <!-- 			SRC comes from the array of images the :key is important for vue to believe its a 'new' DOM element and do the transition -->
    			<div class="theImage" v-bind:style="{'background-image': 'url(' + images[currentID] + ')'}" v-bind:key="currentID"></div>
        </transition>
    	<button @click="toggleImage">
          Toggle Image
        </button>
    </div>