0

I have a with a set of images, all of which are the same height but different width. I need the to stay at a fixed width, and I need the images to slide to the left in an infinite loop. I also need the images to fill the container width, so that even partial images display. The scrolling will happen in steps (e.g. 200px) and at a set interval.

I have looked through a good number of jQuery slideshow plugins, including Cycle, NivoSlider, EasySlider, and half-a-dozen others. But none that I've seen seem to let me know exactly what a client needs wants it to do. I don't need clickable navigation, or centering. Just a continuous filmstrip that scrolls in set-width steps and a set interval.

I was thinking, as far as the continuous-loop part is concerned, is that I could wait until the left-most image has gone out of sight. Originally, I tried just using append(), but the slides jumped when the left-most was moved to the end. So then I tried to append() a clone() of it to the end $('#slides').children(), then remove() it from the beginning of $('#slides').

So far, I have:

<div id="outer" style="width:300px;">
    <div id="slides">
        <img src="/images/slide1.jpg" alt="" style="height:150px;width:200px;" />
        <img src="/images/slide2.jpg" alt="" style="height:200px;width:200px;" />
        <img src="/images/slide3.jpg" alt="" style="height:150px;width:200px;" />
        <img src="/images/slide4.jpg" alt="" style="height:300px;width:200px;" />
    </div>
</div>

and

/* some code ideas taken from easySlider */
(function($){
    $.fn.stepSlider = function() {
        var slideInterval;

        this.each(function() {
            // this will be the width of $('#slides')
            var width = 0;
            // shortcut I saw in easySlider code; liked it, used it.
            var o = $(this);
            // set width of $('#slides') based on total width of child images
            o.children().each(function(){width+=$(this).width();});

            function scroll() {
                o.children().each(
                    function(index){
                        // slide each of the children to the left by 150px
                        $(this).animate({'left':'-=150'}, 2000);
                    }
                );

                // after children have been animated() left, check to see
                // if the left-most child has gone out of sight
                if (o.children(':first-child').position().left + o.children(':first-child').width() < 0) {
                    // cloning the first child now
                    var el = o.children(':first-child').clone(true);
                    // removing the first child
                    o .children(':first-child').remove();
                    // trying to reset the css('left') of all of the children now that the first
                    // element is supposed to be gone
                    o.children().css('left', o.children(':first-child').position().left + o.children(':first-child').width());
                    // appending the clone()
                    o.append(el);
                }
            }

            slideInterval = setInterval(function(){scroll();}, 2000);
        });
    }
})(jQuery);

The sliding works. But the shifting of the first image to the end causes the slides to jump. And then the thing just starts breaking down to the point of stopping altogether.

Any insight is greatly appreciated. No, I'm not completely new to jQuery, but yes: this is my first plugin attempt.

Thanks and best regards

C Fishburn
  • 11
  • 4

2 Answers2

1

I'd suggest you can use the scrollTo plugin (or write your own). The following will scroll an area by 300 px so it doesn't matter what you put in it. You'll have to manage the position and the width of the area to stop at the end or do as you need.

ScrolTo: http://demos.flesler.com/jquery/scrollTo/

JSFiddle: http://jsfiddle.net/YZ9vA/2/

    $(document).ready(function(){
    var areaWidth = 0, x=0;        
    $('#slideshow img').each(function() {
        areaWidth = areaWidth + $(this).width();
    });
    $('#slideshow').width(areaWidth);

    function ScrollMe() {
     x += 300;
     $('#sliders').scrollTo({top:0, left:x},800);

     }


   setInterval(ScrollMe,1000);

});​

Original Answer:

My suggestion to you is to use jquery cycle which will repeat forever. Instead of cycling images, cycle divs that way you can set those widths to 200px and the images inside you can place however you need (centered, width 100%, etc).

http://jquery.malsup.com/cycle/

#slides, #slides div {width:200px;height:300px}
#slides img {width:100%;height:100%}  

<div id="slides">
    <div><img src="/images/slide1.jpg" alt="" style="" /></div>
    <div><img src="/images/slide2.jpg" alt="" style="" /></div>
    <div><img src="/images/slide3.jpg" alt="" style="" /></div>
    <div><img src="/images/slide4.jpg" alt="" style="height:300px;width:200px;" /></div>
<!-- I left the last inline style on, you can use that to override or use css externally -->
</div>

$('#slides').cycle({fx:'scrollHorz', 
                   continuous: 1, 
                   easeIn: 'linear',
                   easeOut: 'linear'});
lucuma
  • 18,247
  • 4
  • 66
  • 91
  • Sorry, but the images aren't the same width and can't be scaled/stretched to width:100%. Each image's aspect ratio needs to be maintained. FWIW, I only used the pixel counts as an example. The actual container is going to be about 960px wide x 150px high (basically a banner), but the 10-15 pictures inside will vary between ~400px to 960px wide and 150px high. Besides, cycle doesn't support stepped scrolling in which an image isn't entirely replaced. I have to show a continuous scrolling filmstrip that scrolls right-to-left in probably 200px or 300px increments. – C Fishburn May 15 '12 at 22:32
  • Both of those still swap out the **entire** photo. My client specifically wants a **stepped transition**. So, let's say the slide viewport is 900px wide by 100px high, and let's say one of the photos is 900 pixels wide. The transition should slide left and only reveal the **first 300-pixels** (width) of the image **then pause**. Then it should shift another 300 showing (now) 600-pixels width **then pause**. And and so on. The slide-left will be smooth due to `$.animate()`. But the scrolling motion cannot be continuous. – C Fishburn May 16 '12 at 01:29
  • I've updated the answer based on your feedback.. It now scrolls some arbitrary distance (set to 300 in my example) and pauses for a second. – lucuma May 16 '12 at 02:28
  • As you can see above, I tried creating my own solution. I posted my second attempt where I append a clone of the :first object, then just remove it from the beginning. That works partially, but positions and smooth scrolling go awry after the first append/remove. My first solution scrolled with increments just fine, paused just fine, _but it didn't loop_. Your most recent example also only ran once, rather than looping. The loop is sort of trivial, given setInterval(). The real problem is the positioning of the elements (getting the first one shifted to the end as it goes out of sight). – C Fishburn May 16 '12 at 06:09
  • I've added a loop in. The code will have to be adapted to your scenario. It probably isn't the most elegant solution but it is rather simple. http://jsfiddle.net/DCdUd/1/ – lucuma May 16 '12 at 12:48
0

So thanks to all of lucuma's help , I have something working.

CSS:

<style type="text/css">
    #outer  {height: 250px; width: 760px; overflow:hidden;}
    #mid    {height: 250px; width: 760px; overflow:hidden;}
    #slides {height: 250px; width: auto; overflow:hidden;}
    #slides img {float: left; height: 250px;} /* height specified just for clarity */
</style>

HTML:

I had to wrap the #slides div in two other divs instead of one. With just one wrapper, the scrollTo plugin ended up setting the divs to 100% width rather than honoring my 760px constraint. Due to time constraints, I didn't investigate this further. I just added the extra div and called it a day.

<div id="outer">
    <div id="mid">
        <div id="slides">
            <img src="images/image1.jpg" alt="" style="width:760px;" />
            <img src="images/image2.jpg" alt="" style="width:529px;" />
            <img src="images/image3.jpg" alt="" style="width:720px;" />
            <img src="images/iamge4.jpg" alt="" style="width:563px;" />
            <!-- my live version has 16 total images -->
        </div>
    </div>
</div>

JavaScript:

<script type="text/javascript" src="scripts/jquery.scrollTo.min.js"></script>
<script type="text/javascript">
    $(function(){
        var areaWidth = 0, x = 0, lelements=$('#slides img').length * -1;

        /* This is the same essential functionality that I had in my original post
         * to set the width of the images container
         */
        function setWidth() {
            $('#slides img').each(function() {
                areaWidth = areaWidth + $(this).width();
            });
            $('#slides').width(areaWidth);
        }

        /* This is also essentially what I was doing before: checking
         * to see if an image is no longer viewable. It appends a clone
         * to the $('#slides'), but it differs from my original idea by
         * not trying to remove any :first element.
         * So $('#slides img').length will continue to increase as the
         * program runs. I was trying to avoid this, but in reality, it
         * probably doesn't matter a ton unless someone is watching the
         * same page for quite a long time.
         */
        function checkIt() {
            $('#slides img').slice(lelements).each(function() {
                if ($(this).position().left + $(this).width() < 0) {
                    $(this).clone().appendTo($('#slides'));
                }
            });
        }            

        /* This is where we use Flesler's scrollTo plugin to handle the motion.
         * I changed scrollTo({}, 3000) to scrollTo({}, {duration: 1000})
         * to slow the transition speed.
         */
        function scrollMe() {
            $('#outer').scrollTo({top:0, left:x}, {duration:1000});

            /* x += 300 was the first line in scrollMe(), but ended up
             * resulting in a 600px slide for the first transition,
             * so I moved it here. The first transition takes longer
             * to occur, but all other transitions are fine
             */
            x += 300;

            checkIt();
            setWidth();
        }

        setInterval(scrollMe, 3000);  
    });
</script>

One other thing I had to do was make sure my first slide was the full width (760px). If it was less than that, then the second image would initially be invisible, then it would suddenly appear in the left space of the container, and then the scrolling would happen. All subsequent images were fine. Obviously, having the first image fill the full width eliminates that artifact.

Thanks again to lucuma - you were a TON of help!

C Fishburn
  • 11
  • 4
  • After all that, the customer decided they didn't want the stepped-motion, but rather a smooth, continuous motion that looped. So my actual call to `scrollTo` ended up looking like this: `$('#outer').scrollTo('{top:0,left:x},{duration:4000,easing:'linear'});` followed by `x += 400;`. – C Fishburn May 18 '12 at 16:23
  • 1
    I'd appreciate you marking my answer if it helped you by clicking the checkbox. – lucuma May 18 '12 at 20:31