Swipeable Carousel with Vanilla Javascript
This is what we will be creating
The HTML and SCSS are not doing anything extraordinary in this example. There is a display: flex; on the track with no wrapping so that each of the cards are displayed in a horizontal fashion. Note that the .carousel__inner class has a width of 70% of the container. This allows for the next and previous cards to be bleeding off the edge.
We flex each card 100% so it fills the container fully. Note there is a .dragging class on the track. This will toggle the transition. We do not want this dragging class when the user is manually dragging the carousel with their mouse or finger. Once they are done dragging, we remove this class so that the desired card gracefully animates to the center.
Let's talk about the javascript portion for this. Initially I detect whether there are touch events. If there are I attach the touchstart touchmove and touchend events. Otherwise, I listen to the mouse events.
When the user makes contact with the track by clicking or pressing down, we first get those coordinates.
this.xDown = e.touches ? e.touches[0].clientX : e.clientX;
this.yDown = e.touches ? e.touches[0].clientY : e.clientY;
As the user drags their cursor or mouse, we then determine the delta from this starting point.
const deltaX = (e.touches ? e.touches[0].clientX : e.clientX) - this.xDown;
const deltaY = (e.touches ? e.touches[0].clientY : e.clientY) - this.yDown;
const absDeltaX = Math.abs(deltaX);
const absDeltaY = Math.abs(deltaY);
if(absDeltaY > absDeltaX) {
return false;
}
We check the absolute value to see whether or not the y-delta is greater than the x. If it is greater, we can probably assume the user is scrolling by and we shouldn't lock the gesture into a carousel transition, but rather allow them to keep scrolling.
Once the user releases their finger or mouse, we get the latest coordinates to determine if they are swiping left or swiping right. Depending on this difference, we add or subtract the index.
const xUp = e.clientX;
const yUp = e.clientY;
const xDiff = this.xDown - xUp;
const yDiff = this.yDown - yUp;
this.track.classList.remove('dragging');
if (Math.abs(xDiff) > Math.abs(yDiff)) { /* most significant */
if (xDiff > 0) {
this.index++;
}
else {
this.index--;
}
}
When we subtract the index, we are actually calling a setter method. In this setter method we determine where we are at in the carousel in terms of position. If the next index is a valid position, we then animate to that desired spot by by multiplying the newIndex by the length of the container. This will give us the appropriate transform value.
set index(i) {
let newIndex = i;
if (newIndex >= this.slides.length-1) {
newIndex = this.slides.length - 1;
}
if (newIndex < 0) {
newIndex = 0;
}
this.transformX = newIndex * -this.track.offsetWidth;
this.track.style.transform = `translateX(${this.transformX}px`;
this._index = newIndex;
}
We hold onto this transform value so we can use it in future dragging events.
And that's it. Feel free to experiment with the codepen to extend or optimize.

