2

I have an animation going here that slides in 3 images onto the screen, holds them there for 30 seconds then slides them back off screen.

My issue currently is, Image 2 will only slide on screen once Image 1 has fully completed it's slide-in animation, and same thing with Image 3 waiting for Image 2 to fully slide in. I want to try to make this animation a bit more 'fluid' looking.

All my images are 400px in length. How can I start sliding image 2 into view once image 1 has crossed the 200px line (50% on screen) instead of it having to wait for image 1 to be 100% on screen. And then do the same thing for image 3 as well, start it sliding in once image 2 has moved 200px to the left.

Here's a simple image to possibly help better illustrate what I want to do here:

enter image description here

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
$(document).ready(function(){
  
  var slideOutTime=1000; // Time it takes to slide onto the screen
  var slideInTime=1000;  // Time it takes to slide off screen
  var slideInAfterTime=30000;  // Hold position on screen for 30 seconds then slide off screen
  var repeatSlidingTime=90000;  // Repeat animation after this amount of time
  
  
  function slideImage(img){
    
    img.animate({left:'0px'},slideOutTime,function(){
      var nxt=img.next();
      if(nxt.length>0){
        slideImage(nxt);
      }else{
        setTimeout(function(){startSliding();},repeatSlidingTime);
      }
      setTimeout(function(){slideBack(img);},slideInAfterTime);
    });
  
    }
  
  function slideBack(img){
     img.animate({left:'-400px'},slideInTime);
  }
  
  function startSliding(){
    slideImage($('.slide:first'));
    }
  
  startSliding();

});

</script>
<html>
    <head>
        <style>
            .wrapper {
                position: relative;
                overflow: hidden;
                width: 500px;
                height: 500px;
            }

            .slide {
              left:-400px;
                position: absolute;
                width: 400px;
                height: 75px;                
            }
          .slide:nth-child(2) {top:60px;}
          .slide:nth-child(3) {top:120px;}

   
        </style>
    </head>
    <body>
        <div class="wrapper">
          <img class="slide" src="http://i.imgur.com/4JqfxNO.png" />
          <img class="slide" src="http://i.imgur.com/ehdAPjk.png" />
          <img class="slide" src="http://i.imgur.com/yQ51oro.png" />
        </div>

    </body>
</html>

Basically just trying to start moving an image into view once the previous image has reached 50% on screen instead of waiting for it to be 100% on screen and then starting its animation.

Mi-Creativity
  • 9,554
  • 10
  • 38
  • 47
Ele Hex
  • 21
  • 3

3 Answers3

1

Delete the slideOutTime variable definition on document ready and change the code to your startSliding function to this:

function startSliding(){
    var slides = $('.slide').length;
    var slideOutTime = 500;
    for (i=1;i <= slides; i++) {
        slideImage($('.slide:nth-child('+i+')'), slideOutTime);
      slideOutTime = slideOutTime + 300;
    }

    }

Here is the updated fiddle

The reason why your current code doesnt work is because you are animating the next image in your callback function, which means the first animation must finish before the callback gets executed

  • 1
    This is exactly how I envisioned it, great! I changed the slideOutTime = slideOutTime + 300 line to a value of 100 instead because I like the animation being faster. One thing I noticed though, after watching the animation for a while it broke after repeating itself after so many times. Everything went out of sync, I captured a gif of it in action: http://imgur.com/a/trOVU – Ele Hex Aug 23 '16 at 18:11
  • That is a very interesting bug. I will take another look at it – Denis Kumbaradzi Aug 23 '16 at 18:16
  • Fixed. Here is the updated fiddle: https://jsfiddle.net/rj0u1awb/5/. I forgot to remove the animation code from the callback function – Denis Kumbaradzi Aug 23 '16 at 18:19
0

As @DenisKumbaradzi mentioned in his answer, you could not start animating the next image in after the current image is 50% in the view because you're doing the next image animation as callback function of the current image animation which means it only starts animation after the first animation is done.


FIRST: Using jQuery Animation:

You can make use of the jQuery delay() function to delay the next slide animation depending on the index value of jQuery each() function like below:

*. Note that I have reduced the amount of slideInAfterTime and slideInAfterTime to have faster animation for demonstration purposes, as well as changing functions code

jsFiddle 1

$(document).ready(function() {

  var slideOutTime = 1000; // Time it takes to slide onto the screen
  var slideInTime = 1000; // Time it takes to slide off screen
  var slideInAfterTime = 5000; // Hold position on screen for 30 seconds then slide off screen
  var repeatSlidingTime = 15000; // Repeat animation after this amount of time
  var delay = 400; // amount of delay between current and next animation
  var slides = $('.wrapper .slide');

  animateImages();
  setInterval(animateImages, repeatSlidingTime);
  
  function animateImages(){
   slides.each(function(index) {
     var del = index * delay;
     slideImageIn($(this), del);
   });
  }

  function slideImageIn(img, del) {
    img.delay(del).animate({left: '0px'}, slideInTime, function() {
      slideImageBack(img);
    });
  }

  function slideImageBack(img) {
    img.delay(slideInAfterTime).animate({ left: '-400px'}, slideInTime);
  }

});
.wrapper { position: relative; overflow: hidden; width: 500px; height: 500px; }
 .slide { left: -400px; position: absolute; width: 400px; height: 75px; }
 .slide:nth-child(2) { top: 60px; }
 .slide:nth-child(3) { top: 120px; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="wrapper">
  <img class="slide" src="//i.imgur.com/4JqfxNO.png" />
  <img class="slide" src="//i.imgur.com/ehdAPjk.png" />
  <img class="slide" src="//i.imgur.com/yQ51oro.png" />
</div>

SECOND: Using GSAP Animation:

Another approach is to use GreenSock Animation Platform (GSAP) library for animation which produces better and smoother animation than jQuery animation, and to make this 50% wait between slides animation we make use of the GSAP staggerTo() function, like below:

*. Please note that GSAP animation functions use timing in seconds not in milliseconds, this why values of slideOutTime, slideInTime, slideInAfterTime and delay are in seconds, while repeatSlidingTime is still in milliseconds because we're not using it in GSAP unction but with setTimeout() function.

jsFiddle 2

$(document).ready(function() {

  var slideOutTime = 1; // Time it takes to slide onto the screen
  var slideInTime = 1; // Time it takes to slide off screen
  var slideInAfterTime = 5; // Hold position on screen for 30 seconds then slide off screen
  var repeatSlidingTime = 15000; // Repeat animation after this amount of time
  var delay = 0.4; 
  var slides = $('.wrapper .slide');
  
  animateImages();

  function animateImages(){
   TweenMax.staggerTo(slides, slideInTime, {
    left: 0
   }, delay);
   TweenMax.staggerTo(slides, slideOutTime,{
    left: '-400px',
     delay: slideInAfterTime
   }, delay, function(){
     setTimeout(animateImages, repeatSlidingTime);
    });   
  } 
});
.wrapper { position: relative; overflow: hidden; width: 500px; height: 500px; }
.slide { left: -400px; position: absolute; width: 400px; height: 75px; }
 .slide:nth-child(2) { top: 60px; }
 .slide:nth-child(3) { top: 120px; }
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/gsap/1.19.0/TweenMax.min.js"></script>

<div class="wrapper">
  <img class="slide" src="//i.imgur.com/4JqfxNO.png" />
  <img class="slide" src="//i.imgur.com/ehdAPjk.png" />
  <img class="slide" src="//i.imgur.com/yQ51oro.png" />
</div>

Edit: In case images don't seem like sliding in after 50% of the previous image in the view is because in GSAP animation there's "easing" by default, if you don't want the easing you can add ease:Linear.easeNone property to both TweenMax lines. Now if check this jsFiddle 3 with no easing you'll see each image slides in exactly when 50% of the previous image is in the view

Mi-Creativity
  • 9,554
  • 10
  • 38
  • 47
  • I see, makes sense thank you. I reduced the slideOutTime, slideInTime, and delay values to make the animation a bit quicker/snappier and it's working very well now. One final thing from me on this little project. The images slide onto the screen one by one just how I wanted, but is there a way to have them slide off screen all together at the same time? – Ele Hex Aug 23 '16 at 18:47
  • sure, in the `slideImageBack()` we subtract the value of `del` from the whole repeating, thus we'll have them all sliding out at the same time [**jsFiddle Example**](https://jsfiddle.net/kpkpx3sL/8/) – Mi-Creativity Aug 23 '16 at 18:53
  • for the GSAP version of animation, to make all images slides out at same time just set replace `delay` in the second `TweenMax` function with **`0`** to get rid of the delay between them on sliding out only, like in this [**jsFiddle Example**](https://jsfiddle.net/pu8p4zq2/2/) – Mi-Creativity Aug 23 '16 at 19:05
0

Instead of trying to start moving an image into view once the previous image has reached 50% on screen, you can insert a delay between animations and don't wait for the previous animation to finish.

Please have a look on my annotatated working example below and see if it works for you. It should also work with as many images you'd like to.

var slideOutTime = 600, // Time it takes to slide onto the screen
  slideInTime = 600, // Time it takes to slide off screen
  animationsDelay = slideOutTime / 2, // Delay we insert between animations
  slideInAfterTime = 3000, // Hold position on screen for 30 seconds then slide off screen
  repeatSlidingTime = 9000; // Repeat animation after this amount of time


function slideImage(img) {

  img.each(function(index) {
    var $this = $(this);

    $this
    // Incrementally delay animations based on each image's index
      .delay(animationsDelay * index)
      .animate({
        left: 0
      }, slideOutTime, function() {
        $this
        // Delay the reverse animation based on how many elements to animate we have
          .delay((img.length - 1) * animationsDelay + slideInAfterTime)
          .animate({
            left: '-400px'
          }, slideInTime, function() {
            if (!($this.next().length)) {
              // After last element reverse animation has ended, we start all over again
              setTimeout(function() {
                startSliding();
              }, repeatSlidingTime);
            }
          });
      });
  });
}

function startSliding() {
  slideImage($('.slide'));
}

startSliding();
.wrapper {
  position: relative;
  overflow: hidden;
  width: 500px;
  height: 500px;
}
.slide {
  left: -400px;
  position: absolute;
  width: 400px;
  height: 75px;
}
.slide:nth-child(2) {
  top: 60px;
}
.slide:nth-child(3) {
  top: 120px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div class="wrapper">
  <img class="slide" src="http://i.imgur.com/4JqfxNO.png">
  <img class="slide" src="http://i.imgur.com/ehdAPjk.png">
  <img class="slide" src="http://i.imgur.com/yQ51oro.png">
</div>

PS: I'd also give it a try using CSS keyframe animation instead of JavaScript.

  • Using the `index` from the `each()` jQuery function and multiply it by the delay to get staggered animation is efficient and it's the same technique I've used in the jQuery part of my answer from yesterday and yes it does work dynamically for any number of images. – Mi-Creativity Aug 24 '16 at 21:46
  • Considering the CSS Animation, well yeah CSS animation is definitely better than jQuery animation (jQuery only not javascript in general), but it has disadvantages sometimes, like in this case the sliding-in, waiting in the view and then sliding out total time is **`32`** seconds (**`1`**, **`30`**, **`1`** respectively), now to have the correct ratio for the keyframes function you will have numbers like **`3.125%`** and **`96.875%`**, beside doing simple calculations every time, you don't know how many decimal places can be used in CSS animation (http://stackoverflow.com/q/10486242/2081719). – Mi-Creativity Aug 24 '16 at 21:49
  • Also remember if you decided to alter the duration of any of the slides animation you need to do calculations again and you may end with different decimal place, like if the whole animation in the OP is **`31`** or **`30`** instead of **`32`** seconds. – Mi-Creativity Aug 24 '16 at 21:51
  • And you need to hardcode the delay for each image in CSS or use javascript to control it dynamically, also AFAIK you can't do the whole animation in the OP using CSS only, you still need javascript to control the repeating every 90 seconds, and to get it precisely you should listen for animation events, like `animationened` event to know when to remove the animation class, which for Chrome 42 and below, Safari 8 and below, Opera 29 and below, Android 4.4.4 and below and blackberry current and below and you need to use the `webkitAnimationEnd`, `mozAnimationEnd` for Firefox 16 and below. – Mi-Creativity Aug 24 '16 at 21:53
  • Beside, the jQuery (1.x series) and GSAP versions of the animation works back to IE6 and other old browsers without prefixes for the CSS, while you need to use prefixes in the CSS for `animation` and `keyframes` if you want to gracefully support older browser (supporting IE9 and below is not an option though), – Mi-Creativity Aug 24 '16 at 21:54
  • *while the above is not mostly an issue currently*, there still people use older browsers especially IE browsers, those who are not tech savvy, some institutions and some countries. so if they are of your audience and you consider supporting them then you either to make another version of your animation and using feature-detection to conditionally support it or you ignore CSS animation as an option. – Mi-Creativity Aug 24 '16 at 21:55
  • Finally, using CSS animation -*controlled by javascript*- to achieve the animation in the OP: delay is hardcoded in the CSS for every element: https://jsfiddle.net/kpkpx3sL/10/ , the delay value is controlled by javascript version https://jsfiddle.net/kpkpx3sL/12/ – Mi-Creativity Aug 24 '16 at 21:58
  • Hey @Mi-Creativity, I'm sorry I didn't get the chance to look over your answer. Thanks for putting so much thought into the CSS alternative. I'll have a look on it once I have the time. – Serban Costinescu Aug 26 '16 at 08:21