mr_Jezy mr_Jezy - 2 months ago 8
CSS Question

CSS Animation won't apply for the second time

I am currently having a problem with CSS animations. A random background is called from an array, shows up and changes and so on. I applied two animation for the image caption id, a slide in and a delayed slide out. The slide in and out runs well for the first time, but when the second background shows up, the caption just appears to the screen without any animation.

This is my test page and below is my code.

HTML code:

<script type="text/javascript">

function loadRandomImage(imgs) {
var index = Math.floor(Math.random() * imgs.length);

console.log("loadRandomImages(): index = "+ index);

$.backstretch(imgs[index].url, {duration: 30000, fade: 1200});
$("#caption").html(imgs[index].caption);
}

var images = new Array(); //array of imgs objects

images[0] = {url: "https://s-media-cache-ak0.pinimg.com/736x/a5/47/45/a5474577f4a4ae93c85db719d0cbafd4.jpg", caption: "Caption0"};
images[1] = {url: "https://s-media-cache-ak0.pinimg.com/736x/e6/41/74/e64174e355f78a0f07e951bcec62ca96.jpg", caption: "Caption1"};
images[2] = {url: "https://media.giphy.com/media/3o7abHrsGbV10rCeze/giphy.gif", caption:"Caption2"};
images[3] = {url: "https://media.giphy.com/media/Bbt5FxRiArl3a/giphy.gif", caption:"Caption3"};

// Preload

setTimeout(loadRandomImage, 1000, images);

// Change images every 3 seconds

setInterval(loadRandomImage, 30000, images);

</script>


<div id="pattern"></div>
<div id="pattern2"></div>

<div id="caption"></div>


CSS code:

#caption {
position: relative;
font: 1.5em Trebuchet, sans-serif;
text-align: center;
margin-left: 75%;
z-index: 56;
color: #ffffff;
background: rgba(0, 0, 0, 0.7);
padding: 8px;
animation: slidein 3s, slideout 3s 27s;
}

#caption:empty
{
display: none;
}

@keyframes slidein {
0% {
margin-left: 100%;
width:100%;
visibility:hidden;
opacity: 0;
}

100% {
margin-left: 75%;
width:100%;
opacity: 1;
visibility:visible;
}
}

@keyframes slideout {
0% {
margin-left: 75%;
width:100%;
opacity: 1;
visibility:visible;
}

100% {
margin-left: 100%;
width:100%;
opacity:0;
visibility:hidden;
}
}

Answer

CSS animations have iteration count (animation-iteration-count) as only 1 when no value is given for that property. Here since you've not specified any value, the animation executes only once (that is on page load). There is no pure CSS way to re-trigger an animation once it has completed its cycle. It has to be removed from the element and then re-attached for it to start all over again.

So, for your case here is what you have to do - (a) Set the animations on #caption using JS on page load as it makes it easier to remove and re-add them (b) Upon completion of the slideout animation, remove both the animations from the element (that is, set animation-name: none) and also set html of #caption to none because :empty selector would only then hide it. (c) As soon as the next image is set on the element (using loadRandomImage function), set the animations back on the element. This would re-trigger the animation and so during each image switch, the caption would slide-in and out.

Note: I've changed some parts in the HTML and JS that are not relevant to this answer (like removing the two div and replacing them with 1, avoiding the $.backstretch and loading image using css() etc. But these are only auxiliary items and will not affect the crux of this answer (which is, to remove and add the animations).

function loadRandomImage(imgs) {
  var index = Math.floor(Math.random() * imgs.length);

  $('#img').css('background-image', 'url(' + images[index].url + ')');
  $('#caption').css({
    'animation-name': 'slidein, slideout',
    'animation-duration': '3s, 3s',
    'animation-delay': '0s, 7s'
  });
  $("#caption").html(imgs[index].caption);
}

var images = new Array(); //array of imgs objects

images[0] = {
  url: "http://lorempixel.com/100/100/nature/1",
  caption: "Caption0"
};
images[1] = {
  url: "http://lorempixel.com/100/100/nature/2",
  caption: "Caption1"
};
images[2] = {
  url: "http://lorempixel.com/100/100/nature/3",
  caption: "Caption2"
};
images[3] = {
  url: "http://lorempixel.com/100/100/nature/4",
  caption: "Caption3"
};

// Preload

setTimeout(loadRandomImage, 1000, images);


$('#caption').on('animationend', function(e) {
  if (e.originalEvent.animationName == 'slideout') {
    $('#caption').css('animation-name', 'none');
    $('#caption').html('');
    setTimeout(function() { /* dummy timeout to make sure browser sees animation as none before adding it again */
      loadRandomImage(images);
    }, 0);
  }
});
#caption {
  position: relative;
  font: 1.5em Trebuchet, sans-serif;
  text-align: center;
  margin-left: 75%;
  z-index: 56;
  color: #ffffff;
  background: rgba(0, 0, 0, 0.7);
  padding: 8px;
}
#caption:empty {
  display: none;
}
@keyframes slidein {
  0% {
    margin-left: 100%;
    width: 100%;
    visibility: hidden;
    opacity: 0;
  }
  100% {
    margin-left: 75%;
    width: 100%;
    opacity: 1;
    visibility: visible;
  }
}
@keyframes slideout {
  0% {
    margin-left: 75%;
    width: 100%;
    opacity: 1;
    visibility: visible;
  }
  100% {
    margin-left: 100%;
    width: 100%;
    opacity: 0;
    visibility: hidden;
  }
}
/* Just for demo */

#img {
  height: 100px;
  width: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="img"></div>
<div id="caption"></div>

The animationend event still requires vendor prefixes in some browsers.

Comments