2

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;
  }
}
Harry
  • 87,580
  • 25
  • 202
  • 214
beamspot
  • 29
  • 1
  • 3

4 Answers4

2

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.

Harry
  • 87,580
  • 25
  • 202
  • 214
  • 1
    Thanks Harry for your help and for tailoring my code, so others can learn from it as well! It works almost perfectly. Just one little problem: the preload works well, choosing the random image, the caption slides in and out, but when the second image is loaded, the caption just isn't showing up. Strange, because then from the third image it works perfectly again. I guess something is happening after the preload. Any suggestion? – beamspot Mar 24 '16 at 17:20
  • @mr_Jezy: You're welcome. Glad that I could help. The issue that you've highlighted seems to be a bit of timing issue and so I have moved the calling of the `loadRandomImage` function also to be within the `animationend` event. The timeout (with 0 seconds) is a dummy just to make sure that removal and re-addition doesn't happen in a single blocking call (otherwise browser doesn't see any change at all and hence won't re-trigger animation). Please mark the answer as accepted if it helped. Its always nice to be recognized for the efforts :D – Harry Mar 24 '16 at 17:33
1

You need to use a callback, which is explained here:

How do create perpetual animation without freezing?

Community
  • 1
  • 1
Arif Burhan
  • 507
  • 4
  • 12
1

I think the animation direction needs to be altered.

These are the possibilities:

animation-direction: normal|reverse|alternate|alternate-reverse|initial|inherit;

I think you need to do one of these:

alternate The animation will be played as normal every odd time (1,3,5,etc..) and in reverse direction every even time (2,4,6,etc...)

alternate-reverse The animation will be played in reverse direction every odd time (1,3,5,etc..) and in a normal direction every even time (2,4,6,etc...)

At the moment it is set as

animation-direction: initial, initial;

Seen here: http://www.w3schools.com/cssref/css3_pr_animation-direction.asp

Gilko
  • 2,280
  • 8
  • 36
  • 45
1

Rather than the Javascript suggestions already provided, you could do a straight CSS solution.

  • Just set animation-iteration-count to "infinite" (to continuously alternate the 2 elements, or an integer for a set number of repeats)

If you want staggered / alternating animations:

  • Use an animation-delay (matching the animation-duration) on the second element so it doesn't appear until the first element animation has completed
  • Build a delay onto the end of your animation (revert to original state @ 50%) so that the first element stays hidden while the second animates.
brichins
  • 3,825
  • 2
  • 39
  • 60