tl;dr - JSBin Example: http://jsbin.com/ufoceq/8/
A simple approach to create an infinite image slider without too much effort is as follows: let say for the sake of simplicity that you have <n>
images to slide in a loop, so that after the n
th image the next one to visualize is the 1
st (and vice-versa).
The idea is to create a clone of first and last image so that
- the clone of the last image is prepended before the first one;
- the clone of the first image is appended after the last one.
Whatever is the amount of your images, you will need to append at most only 2 cloned elements.
Again for the simplicity, let say that all images are 100px
wide and they're wrapped in a container that you move left/right into a clipped mask with overflow: hidden
. Then, all images can be easily aligned in a row with display: inline-block
and white-space: nowrap
set on the container (with flexbox
now it is even easier).
For n = 4
The DOM structure would be something like this:
offset(px) 0 100 200 300 400 500
images | 4c | 1 | 2 | 3 | 4 | 1c
/* ^^ ^^
[ Clone of the last image ] [ Clone of the 1st image ]
*/
At the beginning, your container will be positioned with left: -100px
(or also margin-left: -100px
or even better (for a matter of performance) transform: translateX(-100px)
), so the slider can show the first image. To switch from an image to another you will need to apply a javascript animation over the same property you've previously chosen.
When your slider is currently at the 4th image, you have to switch from image 4
to 1c
, so the idea is to execute a callback at the end of the animation that soon reposition your slider wrapper at the real 1st image offset (e.g. you set left: -100px
to the container)
This is analogous when your slider is currently positioned on the 1st element: to show the previous image you just need to perform an animation from image 1
to 4c
and when animation has been completed you just move the container so the slider is istantly positioned at the 4th image offset (e.g. you set left: -400px
to the container).
You can see the effect on the above fiddle: this is the minimal js/jquery
code I used (of course the code can be even optimized so the width of the items is not hardcoded into the script)
$(function() {
var gallery = $('#gallery ul'),
items = gallery.find('li'),
len = items.length,
current = 1, /* the item we're currently looking */
first = items.filter(':first'),
last = items.filter(':last'),
triggers = $('button');
/* 1. Cloning first and last item */
first.before(last.clone(true));
last.after(first.clone(true));
/* 2. Set button handlers */
triggers.on('click', function() {
var cycle, delta;
if (gallery.is(':not(:animated)')) {
cycle = false;
delta = (this.id === "prev")? -1 : 1;
/* in the example buttons have id "prev" or "next" */
gallery.animate({ left: "+=" + (-100 * delta) }, function() {
current += delta;
/**
* we're cycling the slider when the the value of "current"
* variable (after increment/decrement) is 0 or when it exceeds
* the initial gallery length
*/
cycle = (current === 0 || current > len);
if (cycle) {
/* we switched from image 1 to 4-cloned or
from image 4 to 1-cloned */
current = (current === 0)? len : 1;
gallery.css({left: -100 * current });
}
});
}
});
});
As mentioned before, this solution doesn't require really much effort and talking about performance, comparing this approach to a normal slider without looping, it only requires to make two additional DOM insertion when the slider is initialized and some (quite trivial) extra logic to manage a backward/forward loop.
Here is another example when you see two elements at once: in that case you need to clone more elements and make some simple changes to the logic
https://codepen.io/fcalderan/pen/bGbjZdz
I don't know if a simpler or better approach exists, but hope this helps anyway.
Note: if you need to also have a responsive gallery, maybe this answer may help too