I am trying to create a vertical carousel using vanilla JavaScript and CSS. I know that jQuery has a carousel library but I want to have a go at building this from scratch using
An alternative to using CSS transform
properties is to give the carousel absolute positioning inside a wrapper div
and manipulate the carousel's top
property. Then you can use any easing function you like to animate the sliding motion. In the snippet below, I use cubic easing in/out.
A tricky thing to watch out for is the order in which you rotate the images and perform the sliding animation. When you want to show the next picture below, you have to:
To show the next picture above:
In the following snippet, you can set the width of the carousel by adjusting Carousel.width
at the top of the script. (Although the image height doesn't have to be the same as the image width, I do assume that all images have the same dimensions.) You can also play around with the Carousel.numVisible
and Carousel.duration
parameters.
var Carousel = {
width: 100, // Images are forced into a width of this many pixels.
numVisible: 2, // The number of images visible at once.
duration: 600, // Animation duration in milliseconds.
padding: 2 // Vertical padding around each image, in pixels.
};
function rotateForward() {
var carousel = Carousel.carousel,
children = carousel.children,
firstChild = children[0],
lastChild = children[children.length - 1];
carousel.insertBefore(lastChild, firstChild);
}
function rotateBackward() {
var carousel = Carousel.carousel,
children = carousel.children,
firstChild = children[0],
lastChild = children[children.length - 1];
carousel.insertBefore(firstChild, lastChild.nextSibling);
}
function animate(begin, end, finalTask) {
var wrapper = Carousel.wrapper,
carousel = Carousel.carousel,
change = end - begin,
duration = Carousel.duration,
startTime = Date.now();
carousel.style.top = begin + 'px';
var animateInterval = window.setInterval(function () {
var t = Date.now() - startTime;
if (t >= duration) {
window.clearInterval(animateInterval);
finalTask();
return;
}
t /= (duration / 2);
var top = begin + (t < 1 ? change / 2 * Math.pow(t, 3) :
change / 2 * (Math.pow(t - 2, 3) + 2));
carousel.style.top = top + 'px';
}, 1000 / 60);
}
window.onload = function () {
document.getElementById('spinner').style.display = 'none';
var carousel = Carousel.carousel = document.getElementById('carousel'),
images = carousel.getElementsByTagName('img'),
numImages = images.length,
imageWidth = Carousel.width,
aspectRatio = images[0].width / images[0].height,
imageHeight = imageWidth / aspectRatio,
padding = Carousel.padding,
rowHeight = Carousel.rowHeight = imageHeight + 2 * padding;
carousel.style.width = imageWidth + 'px';
for (var i = 0; i < numImages; ++i) {
var image = images[i],
frame = document.createElement('div');
frame.className = 'pictureFrame';
var aspectRatio = image.offsetWidth / image.offsetHeight;
image.style.width = frame.style.width = imageWidth + 'px';
image.style.height = imageHeight + 'px';
image.style.paddingTop = padding + 'px';
image.style.paddingBottom = padding + 'px';
frame.style.height = rowHeight + 'px';
carousel.insertBefore(frame, image);
frame.appendChild(image);
}
Carousel.rowHeight = carousel.getElementsByTagName('div')[0].offsetHeight;
carousel.style.height = Carousel.numVisible * Carousel.rowHeight + 'px';
carousel.style.visibility = 'visible';
var wrapper = Carousel.wrapper = document.createElement('div');
wrapper.id = 'carouselWrapper';
wrapper.style.width = carousel.offsetWidth + 'px';
wrapper.style.height = carousel.offsetHeight + 'px';
carousel.parentNode.insertBefore(wrapper, carousel);
wrapper.appendChild(carousel);
var prevButton = document.getElementById('prev'),
nextButton = document.getElementById('next');
prevButton.onclick = function () {
prevButton.disabled = nextButton.disabled = true;
rotateForward();
animate(-Carousel.rowHeight, 0, function () {
carousel.style.top = '0';
prevButton.disabled = nextButton.disabled = false;
});
};
nextButton.onclick = function () {
prevButton.disabled = nextButton.disabled = true;
animate(0, -Carousel.rowHeight, function () {
rotateBackward();
carousel.style.top = '0';
prevButton.disabled = nextButton.disabled = false;
});
};
};
body {
font-family: sans-serif;
}
.buttons {
margin: 5px 0;
}
button {
font-size: 14px;
display: inline;
padding: 3px 6px;
border: 2px solid #ccc;
background: #fff;
border-radius: 5px;
outline: none;
}
button:hover {
border: 2px solid #888;
background: #ffe;
cursor: pointer;
}
#carouselWrapper {
position: relative;
overflow: hidden;
}
#carousel {
position: absolute;
visibility: hidden;
}
<div id="spinner">
Loading...
</div>
<div id="carousel">
<img src="http://malsup.github.io/images/beach1.jpg">
<img src="http://malsup.github.io/images/beach2.jpg">
<img src="http://malsup.github.io/images/beach3.jpg">
<img src="http://malsup.github.io/images/beach4.jpg">
<img src="http://malsup.github.io/images/beach5.jpg">
<img src="http://malsup.github.io/images/beach9.jpg">
</div>
<div class="buttons">
<button id="prev">↑ Prev</button>
<button id="next">↓ Next</button>
</div>