Jascha Goltermann
Jascha Goltermann

Reputation: 1124

How to add fade in and fade out when changing background-image with javascript or jQuery

I have four DIVs with different background-images. I am trying to get the DIVs to shuffle through randomly selected background-images every 2 seconds but only one DIV at a time.

I have put together this vanilla javascript with the help of others while searching for a solution, but I am running into the following problems:

  1. The change of the background-image is very abrupt. I would like to have a fade-out -> fade-in effect when the change happens.
  2. I would like to prevent the case that it can select a background-image that is already used in that moment by one of the DIVs.
  3. I would like to convert this code to jQuery but I am really stuck with that task.

Here is the code I have so far, any help is very much appreciated.

var urls = [
  'url(https://placekitten.com/g/350/150)',
  'url(https://placekitten.com/g/200/600)',
  'url(https://placekitten.com/g/550/250)',
  'url(https://placekitten.com/g/700/300)',
  'url(https://placekitten.com/g/300/550)',
  'url(https://placekitten.com/g/400/200)',
  'url(https://placekitten.com/g/350/750)'
];
var active = Math.floor(Math.random() * (urls.length));
setInterval(function() {
  let rand = Math.floor(Math.random() * 4);
  if (rand <= 2 && rand == document.getElementsByClassName('image')[0].getAttribute("data-changed")) {
    rand = rand + 1;
  } else if (rand == 2 && rand == document.getElementsByClassName('image')[0].getAttribute("data-changed")) {
    rand = rand - 1;
  }

  document.getElementsByClassName('image')[rand].style.backgroundImage = urls[active];
  document.getElementsByClassName('image')[0].setAttribute("data-changed", rand);

  active++;
  if (active == urls.length) active = 0;
  //})
}, 2000);
.image {
  background-size: cover;
  display: inline-block;
  width: 200px;
  height: 200px;
  border: 1px solid red;
}

.one { 
  background-image: url(https://placekitten.com/g/350/150); 
}
.two { 
  background-image: url(https://placekitten.com/g/300/550); 
}
.three { 
  background-image: url(https://placekitten.com/g/400/200); 
}
.four { 
  background-image: url(https://placekitten.com/g/350/750); 
}
<div class="image one"></div>
<div class="image two"></div>
<div class="image three"></div>
<div class="image four"></div>

And here is the code on jsFiddle.

Upvotes: 0

Views: 975

Answers (2)

Dony
Dony

Reputation: 1979

I modified your code just a bit to make this new version, that use Fade out / Fade In functions from here, using visibility CSS attribute. This will just make the image disappear, the background will be visible a moment, and then the new image will appear. There are many ways of doing a Fade effect, and this is one of them. You could want something else (for example, the image becomes black and then the new image appears). If you want to try another types of Fade, you can search through CSS filters list.

var urls = [
  'url(https://placekitten.com/g/350/150)',
  'url(https://placekitten.com/g/200/600)',
  'url(https://placekitten.com/g/550/250)',
  'url(https://placekitten.com/g/700/300)',
  'url(https://placekitten.com/g/300/550)',
  'url(https://placekitten.com/g/400/200)',
  'url(https://placekitten.com/g/350/750)'
];
var active = Math.floor(Math.random() * (urls.length));

setInterval(function() {
  let rand = Math.floor(Math.random() * 4);
  if (rand <= 2 && rand == document.getElementsByClassName('image')[0].getAttribute("data-changed")) {
    rand = rand + 1;
  } else if (rand == 2 && rand == document.getElementsByClassName('image')[0].getAttribute("data-changed")) {
    rand = rand - 1;
  }


  document.getElementsByClassName('image')[rand].classList.remove("visible");
  document.getElementsByClassName('image')[rand].classList.add("hidden");

  setTimeout(function(){
    document.getElementsByClassName('image')[rand].style.backgroundImage = urls[active];
    document.getElementsByClassName('image')[0].setAttribute("data-changed", rand);

    document.getElementsByClassName('image')[rand].classList.remove("hidden");
    document.getElementsByClassName('image')[rand].classList.add("visible");

  },400); // 400 ms because animation duration is 0.4s

  active++;
  if (active == urls.length) active = 0;
  //})
}, 2000);
.image {
  background-size: cover;
  display: inline-block;
  width: 200px;
  height: 200px;
  border: 1px solid red;
}

.one { 
  background-image: url(https://placekitten.com/g/350/150); 
}
.two { 
  background-image: url(https://placekitten.com/g/300/550); 
}
.three { 
  background-image: url(https://placekitten.com/g/400/200); 
}
.four { 
  background-image: url(https://placekitten.com/g/350/750); 
}

.visible {
      opacity: 1;
      transition: opacity 0.4s ease;
}

.hidden {
  opacity: 0;
  transition: opacity 0.4s ease;
}
<div class="image visible one"></div>
<div class="image visible two"></div>
<div class="image visible three"></div>
<div class="image visible four"></div>

However, this is not the best way of doing it. So I modified the code, and added some JQuery stuff.

var urls = [
  'url(https://placekitten.com/g/350/150)',
  'url(https://placekitten.com/g/200/600)',
  'url(https://placekitten.com/g/550/250)',
  'url(https://placekitten.com/g/700/300)',
  'url(https://placekitten.com/g/300/550)',
  'url(https://placekitten.com/g/400/200)',
  'url(https://placekitten.com/g/350/750)'
];

// Select the next image, may be one of the actual displayed images
var active = Math.floor(Math.random() * (urls.length));


setInterval(function() {
  // Select randomnly the next div to change
  var rand = Math.floor(Math.random() * 4);
  
  // Store this list, so that we only make one call to the page
  // equiv : document.getElementsByClassName('image')
  let images = $('.image');
  
  // equiv : images[0].getAttribute("data-changed")
  let datachanged = images.attr("data-changed");
  
  // This conditions work, but their is a better way of doing it. See comment below the snippet
  if (rand <= 2 && rand == datachanged ) {
    rand += 1;
  } else if (rand >= 2 && rand == datachanged ) {
    rand -= 1;
  }

  // Jquery selector for the targetted div
  let current = $('.image:nth-child('+(rand+1)+')');
  
  // Now we can use JQuery methods, such as toggleClass
  current.toggleClass("visible hidden");
  
  // The fade effect takes 0.4ms, or 400ms
  // So we use a setTimeout to change the bg in 400ms and not immediatly
  // Once the background is changed, the "visible" class we be added
  // If you want to change the duration, remember to also change it in the CSS
  setTimeout(function(){
  
    // equiv : images[rand].style.backgroundImage = urls[active];
    current.css('background-image', urls[active]);
    
    
    images[0].setAttribute("data-changed", rand);
  
    current.toggleClass("hidden visible");
  
  },400); // 400 ms because CSS animation duration is 0.4s

  // Change active value so that the background will not be same next time
  active++;
  
  // Faster way to write if(...) { active = 0 }
  active = (active == urls.length) ? 0 : active ;
  

}, 2000);
.image {
  background-size: cover;
  display: inline-block;
  width: 200px;
  height: 200px;
  border: 1px solid red;
  transition: opacity 0.4s ease;
}

.one { 
  background-image: url(https://placekitten.com/g/350/150); 
}
.two { 
  background-image: url(https://placekitten.com/g/300/550); 
}
.three { 
  background-image: url(https://placekitten.com/g/400/200); 
}
.four { 
  background-image: url(https://placekitten.com/g/350/750); 
}

.visible {
      opacity: 1;
}

.hidden {
  opacity: 0;
}
<!-- JQuery 3.3.1 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
  <div class="image one" data-changed="0"></div>
  <div class="image two"></div>
  <div class="image three"></div>
  <div class="image four"></div>
</div>

Also, note that you could change

rand = Math.floor(Math.random() * 4);
if (rand <= 2 && rand == datachanged ) {
   rand += 1;
} else if (rand >= 2 && rand == datachanged ) {
  rand -= 1;
 }

by

while(rand == datachanged)
  rand = Math.floor(Math.random() * 4);

So that you have a better "random".

I put a lot of comment, I hope it helped.

Upvotes: 1

Dibakar Halder
Dibakar Halder

Reputation: 213

var urls = [
  'url(https://placekitten.com/g/350/150)',
  'url(https://placekitten.com/g/200/600)',
  'url(https://placekitten.com/g/550/250)',
  'url(https://placekitten.com/g/700/300)',
  'url(https://placekitten.com/g/300/550)',
  'url(https://placekitten.com/g/400/200)',
  'url(https://placekitten.com/g/350/750)'
];
var active = Math.floor(Math.random() * (urls.length));
setInterval(function() {
  let rand = Math.floor(Math.random() * 4);
  if (rand <= 2 && rand == document.getElementsByClassName('image')[0].getAttribute("data-changed")) {
    rand = rand + 1;
  } else if (rand == 2 && rand == document.getElementsByClassName('image')[0].getAttribute("data-changed")) {
    rand = rand - 1;
  }
  
  const imageElements = document.getElementsByClassName('image')
  
  for(var i = 0; i < imageElements.length; i++){
  imageElements[i].className =  imageElements[i].className.replace('animate-fade-in','')
  }

  imageElements[rand].style.backgroundImage = urls[active];
  
  imageElements[rand].className = "image animate-fade-in";

  imageElements[0].setAttribute("data-changed", rand);

  active++;
  if (active == urls.length) active = 0;
  //})
}, 2000);
.image {
  background-size: cover;
  display: inline-block;
  width: 200px;
  height: 200px;
  border: 1px solid red;
}

.one { 
  background-image: url(https://placekitten.com/g/350/150); 
}
.two { 
  background-image: url(https://placekitten.com/g/300/550); 
}
.three { 
  background-image: url(https://placekitten.com/g/400/200); 
}
.four { 
  background-image: url(https://placekitten.com/g/350/750); 
}

.animate-fade-in {
  animation: fadeIn 0.5s linear;
  animation-fill-mode: both;
}

@keyframes fadeIn {
  0% {
    opacity: 0;
  }

  50% {
    opacity: 0.5;
  }

  100% {
    opacity: 1;
  }
}
<div class="image one animate-fade-in"></div>
<div class="image two animate-fade-in"></div>
<div class="image three animate-fade-in"></div>
<div class="image four animate-fade-in"></div>

Upvotes: 0

Related Questions