Infinite All-CSS Scrolling Slideshow

Chris Coyier //

Just for kicks I wanted to see if I could make a row of images animate across a page and repeat indefinitely. Turns out it's really not that hard. The way I did it was to make one big long graphic where the first part and the last part are visually identical.

Then you animate the left position of that image until it appears it's completed one full cycle (but really is just moved far enough to look identical), then it quickly warps back to the original position and starts over.

We just need an outer wrapper to be relative positioned and hide the overflow, then an inside container in which to animate (which is the image).

<div class="slideshow">
   <div class="images">
       A slideshow of images in here (whatever you want to say for screen readers)
   </div>
</div>
.slideshow {
  position: relative;
  overflow: hidden;
}
.images {
  background: url(slideshow.jpg);
  position: absolute;
  left: 0;
  top: 0;
  height: 100%;
  width: 300%;
  -webkit-animation: slideshow 10s linear infinite;
  -moz-animation:    slideshow 10s linear infinite;
}
@-webkit-keyframes slideshow {
  0%    { left: 0; }
  100%  { left: -200%; }
}
@moz-keyframes slideshow {
  0%    { left: 0; }
  100%  { left: -200%; }
}

View Demo

Let's squeeze some better performance out of this thing, cowboy.

The above code should work fine, but it might be a little choppy depending on the browser/platform/version/resources available. We can get better performance (at least in WebKit / Mobile WebKit) by telling the browser we are going to be using 3D transforms (even though we aren't) and then using translateX instead of left.

NOTE: This trick specifically increases the performance (that you normally wouldn't get) out of 2D transforms and opacity changes. If we weren't changing over to using translateX, we wouldn't see any benefit.

.images {
   ...
   
   /* Hey browser, use your GPU */
   -webkit-transform: translate3d(0, 0, 0);
}

@-webkit-keyframes moveSlideshow {
    0%   { 
        -webkit-transform: translateX(0);  
    }    
    100% { 
        -webkit-transform: translateX(-200%);  
    }
}
@-moz-keyframes moveSlideshow {
    0%   { 
        -moz-transform:    translateX(0); 
    }    
    100% { 
        -moz-transform:    translateX(-200%); 
    }
}

I'm not a huge fan of the fact that we essentially need to trick the browser into better performance, but as CSS folks I guess we're used to that kind of thing. I think the original way we wrote it is fine and the browser should recognize when it can be more performant and do that.

Adding Fancies

With my slideshow, I wanted to do two additional cool effects beyond just scrolling the images by. When you rolled over the slideshow it would:

  1. Speed up
  2. Change from black & white to color

To speed it up, you might just think, we'll just reduce the duration on hover! And thus:

.slideshow:hover .images {
   -webkit-animation-duration: 5s;
   -moz-animation-duration:    5s;
} 

But this is going to be jerky! By changing the animation duration you create a new timeline of what properties should be at what values and when. It figures out those new values and jumps to where they should be immediately. It does not just "speed up" from it's exact current position. I'm afraid I don't know a really solid way around this.

Instead, we'll just adjust the concept of "speeding up". If we set two slideshows on top of each other, each one running at different speeds, then hide/show the top-most faster-running on on hover, we can get a pretty decent "speeding up" effect. We'll use transitions to make the transition smoother.

<div class="slideshow">
   <div class="images-1">
       A slideshow of images in here (whatever you want to say for screen readers)
   </div>
   <div class="images-2">
   </div>
</div>
.slideshow > div {
   ...

    -moz-transition:  opacity 0.5s ease-out; 
       -o-transition: opacity 0.5s ease-out; 
  -webkit-transition: opacity 0.5s ease-out; 
      -ms-transition: opacity 0.5s ease-out; 
    
    /* Slow */
    -webkit-animation: moveSlideshow 60s linear infinite;
    -moz-animation:    moveSlideshow 60s linear infinite;
    
    ...
}

.images-1 {
    /* Fast */
    -webkit-animation: moveSlideshow 20s linear infinite;
    -moz-animation:    moveSlideshow 20s linear infinite;
}

.slideshow:hover .images-2 {
    opacity: 0;
}

For the black and white thing, what we'll just make the top-most slideshow black and white and the lower/faster slideshow color. To save an HTTP Request, we'll make both of them the same image, and just shift the background position.

Just one big image
.images-1 {
    /* Sprite */
    background-position: 0 200px;

    ...
}

So that should do it!

View Demo   Download Files

Origin

This idea came the Wufoo Hearts Tech Events page we recently did, where I wanted to show off images from several of the events we've been at recently. And, admittedly, I just wanted to play with this idea.