Testing https://github.com/swieder227/POJS-carousel
A Pen by Andreas Borgen on CodePen.
<div id="main"> | |
<!-- Carousel --> | |
<div class="carousel-main carousel-1" data-slide-index="2"> | |
<!-- Slides --> | |
<div class="carousel-item" data-slide-index="0"> | |
<img src="https://www.fillmurray.com/810/400" /> | |
</div> | |
<div class="carousel-item" data-slide-index="1"> | |
<img src="https://www.fillmurray.com/620/800" /> | |
</div> | |
<div class="carousel-item" data-slide-index="2"> | |
<img src="https://www.fillmurray.com/830/600" /> | |
</div> | |
<div class="carousel-item" data-slide-index="3"> | |
<img src="https://www.fillmurray.com/840/600" /> | |
</div> | |
<!-- Controls --> | |
<button class="carousel-btn" data-dir="_L"></button> | |
<button class="carousel-btn" data-dir="_R"></button> | |
<!-- Dots --> | |
<div class="carousel-dots"> | |
<div class="carousel-dot" data-slide-index="0"></div> | |
<div class="carousel-dot" data-slide-index="1"></div> | |
<div class="carousel-dot" data-slide-index="2"></div> | |
<div class="carousel-dot" data-slide-index="3"></div> | |
</div> | |
</div> | |
</div> | |
(function() { | |
"use strict"; | |
var CSS_ANIM_NEXT = "next", | |
CSS_ANIM_PREV = "prev", | |
CSS_ANIM_NODOUBLETAP = "preventDoubleTap", | |
CSS_ACTIVE = "active", | |
ATTR_GOLEFT = "_L", | |
ATTR_GORIGHT = "_R"; | |
var Carousel = function(obj) { | |
this.obj = document.querySelector(obj); | |
this.slide_index; | |
this.slide_length; | |
this.slide_current_obj; | |
this._init(); | |
}; | |
Carousel.prototype = { | |
constructor: Carousel, | |
_init : function() { | |
// start at [data-slide-index] | |
this.slide_index = this.obj.getAttribute("data-slide-index") ? parseInt(this.obj.getAttribute("data-slide-index")) : 0; | |
// starting obj | |
this._updateCurrentSlideObj(); | |
this.slide_current_obj.classList.add(CSS_ACTIVE); | |
// store length | |
this.slide_length = this.obj.querySelectorAll(".carousel-item").length; | |
// animation end event to use | |
//this.animationEnd = this.whichAnimationEvent(); | |
// go-go-gadget-eventHandlers! | |
this._setupHandlers(); | |
// add swipe detection | |
this._swipeSetup(); | |
}, | |
_setupHandlers : function() { | |
var self = this, | |
carousel = this.obj; | |
function slide(e) { | |
if(e.changedTouches) { | |
//Don't turn this "touch" into a "click": | |
e.preventDefault(); | |
//Don't interfere with swiping, which is handled in _swipeSetup() | |
// e.stopPropagation(); | |
var touch = e.changedTouches[0], | |
elm = document.elementFromPoint(touch.clientX, touch.clientY); | |
//If the touch has moved off the button, this is a *swipe*, and we're done here: | |
if((e.currentTarget !== elm) && (!e.currentTarget.contains(elm))) { | |
return; | |
} | |
} | |
var dir = this.getAttribute("data-dir"); | |
self._handleSwipe(dir === ATTR_GORIGHT); | |
} | |
var buttons = this.obj.querySelectorAll(".carousel-btn"); | |
for(var i = 0; i < buttons.length; i++) { | |
buttons[i].addEventListener("click", slide); | |
//Also add touch events, to respond faster to the click on touch screens: | |
buttons[i].addEventListener("touchend", slide); | |
} | |
var dots = this.obj.querySelectorAll(".carousel-dot"); | |
for(var i = 0; i < dots.length; i++) { | |
dots[i].addEventListener("click", function() { self._slideJump(this.getAttribute("data-slide-index")) }); | |
} | |
function endSlide(e) { | |
//console.log(e.target, e.currentTarget); | |
var slide = e.target; | |
slide.classList.remove(CSS_ANIM_NEXT); | |
slide.classList.remove(CSS_ANIM_PREV); | |
// remove top level class | |
carousel.classList.remove(CSS_ANIM_NODOUBLETAP); | |
} | |
carousel.addEventListener("webkitAnimationEnd", endSlide); | |
carousel.addEventListener( "animationend", endSlide); | |
}, | |
_updateCurrentSlideDot : function () { | |
// update dots | |
var dots = this.obj.querySelectorAll(".carousel-dot"); | |
for(var i = 0; i < dots.length; i++){ | |
if(i === this.slide_index) { | |
dots[this.slide_index].classList.add(CSS_ACTIVE); | |
} else { | |
dots[i].classList.remove(CSS_ACTIVE); | |
} | |
} | |
}, | |
_updateCurrentSlideObj : function() { | |
// get current slide from DOM | |
this.slide_current_obj = this.obj.querySelector(".carousel-item[data-slide-index='"+this.slide_index+"']"); | |
// keep dots concurrent with slides | |
this._updateCurrentSlideDot(); | |
}, | |
/* | |
Sliding Controls | |
*/ | |
// Main movement/animation fn. Applies next/prev & active classes to correct .carousel-item's. | |
_slide : function(dir) { | |
// add preventDoubleTap to prevent double press | |
var carousel = this.obj; | |
carousel.classList.add(CSS_ANIM_NODOUBLETAP); | |
// set diretion-based vars. these classes apply left/right css animations | |
var class_for_current = (dir === ATTR_GORIGHT) ? CSS_ANIM_PREV : CSS_ANIM_NEXT; | |
var class_for_target = (dir === ATTR_GORIGHT) ? CSS_ANIM_NEXT : CSS_ANIM_PREV; | |
// anim out current | |
var current_slide = this.slide_current_obj; | |
current_slide.classList.add(class_for_current); | |
current_slide.classList.remove(CSS_ACTIVE); | |
// anim in next | |
var target_slide = this.obj.querySelector(".carousel-item[data-slide-index='"+this.slide_index+"']"); | |
target_slide.classList.add(class_for_target); | |
target_slide.classList.add(CSS_ACTIVE); | |
// update current slide | |
this._updateCurrentSlideObj(); | |
}, | |
// slide Carousel one item to _L | |
_slideLeft : function() { | |
// if index == 0, set to length, else index-- | |
if(this.slide_index === 0) { | |
this.slide_index = this.slide_length - 1; | |
} else { | |
this.slide_index -= 1; | |
} | |
this._slide(ATTR_GOLEFT); | |
}, | |
// slide Carousel one item to _R | |
_slideRight : function() { | |
// if index == max, set to 0, else index++ | |
if(this.slide_index === this.slide_length - 1){ | |
this.slide_index = 0; | |
} else { | |
this.slide_index += 1; | |
} | |
this._slide(ATTR_GORIGHT); | |
}, | |
// Go directly to slide param:'jumpTo'. Animating in correct direction. | |
_slideJump : function(jumpTo) { | |
var carousel = this.obj; | |
jumpTo = parseInt(jumpTo); | |
if(jumpTo === carousel.slide_index || jumpTo > this.slide_length || jumpTo < 0) { | |
console.error("invalid slide index. wtf m8?"); | |
return false; | |
} else if (jumpTo > this.slide_index) { | |
this.slide_index = jumpTo; | |
this._slide(ATTR_GORIGHT); | |
} else { | |
this.slide_index = jumpTo; | |
this._slide(ATTR_GOLEFT); | |
} | |
}, | |
/* | |
Swipe Detection | |
*/ | |
_swipeSetup : function() { | |
var self = this, | |
touchsurface = this.obj, | |
startX, | |
startY, | |
threshold, //required min distance traveled to be considered swipe | |
allowedTime = 1000, // maximum time allowed to travel that distance | |
startTime; | |
touchsurface.addEventListener("touchstart", function(e) { | |
//Would prevent clicks on prev/next buttons.. | |
// e.preventDefault(); | |
var touchobj = e.changedTouches[0]; | |
startX = touchobj.pageX; | |
startY = touchobj.pageY; | |
startTime = new Date().getTime(); // record time when finger first makes contact with surface | |
threshold = touchsurface.clientWidth/4; | |
}); | |
touchsurface.addEventListener("touchmove", function(e) { | |
e.preventDefault(); // prevent scrolling when inside DIV | |
if(!startTime) { return; } | |
//}); | |
// | |
//touchsurface.addEventListener("touchend", function(e) { | |
//Would prevent clicks on prev/next buttons.. | |
// e.preventDefault() | |
var touchobj = e.changedTouches[0], | |
distX = touchobj.pageX - startX, // get total dist traveled by finger while in contact with surface | |
distY = touchobj.pageY - startY, // get total dist traveled by finger while in contact with surface | |
elapsedTime = new Date().getTime() - startTime; // get time elapsed | |
// check that elapsed time is within specified, horizontal dist traveled >= threshold, and vertical dist traveled < horizontal distance | |
var swipeBool = (elapsedTime <= allowedTime) && | |
(Math.abs(distX) >= threshold) && | |
(Math.abs(distX) > Math.abs(distY)); | |
if(swipeBool) { | |
self._handleSwipe(distX < 0); | |
startTime = 0; | |
} | |
}); | |
}, | |
_handleSwipe : function(goRight) { | |
if (goRight) | |
this._slideRight(); | |
else { | |
this._slideLeft(); | |
} | |
}, | |
}; | |
// Exposed Class | |
self.Carousel = Carousel; | |
})(); | |
new Carousel(".carousel-1"); |
/* Custom CSS */ | |
#main { | |
max-width: 800px; | |
margin: auto; | |
} | |
img { | |
display: block; | |
position: absolute; | |
top:0;left:0;bottom:0;right:0; | |
max-width: 80%; | |
max-height: 90%; | |
margin: auto; | |
} | |
/* Carousel CSS */ | |
/* | |
Basic Carousel layout | |
*/ | |
.carousel-main { | |
position: relative; | |
width: 100%; | |
padding-bottom: 60%; | |
margin: auto; | |
overflow: hidden; | |
} | |
.carousel-main.preventDoubleTap { | |
pointer-events: none; | |
} | |
.carousel-item { | |
position: absolute; | |
width: 100%; | |
height: 100%; | |
top: 0; | |
opacity: 0; | |
} | |
/* | |
Animating carousel-items | |
*/ | |
.carousel-item.active, | |
.carousel-item.prev, | |
.carousel-item.next { | |
opacity: 1; | |
animation-duration: 0.5s; | |
animation-timing-function: ease-in-out; | |
animation-fill-mode: forwards; | |
animation-iteration-count: 1; | |
animation-delay: 0s; | |
} | |
/* slide("_R") */ | |
@keyframes slideLeftOut { | |
0% { transform: translateX(0%);} | |
100% { transform: translateX(-100%);} | |
} | |
@keyframes slideRightIn { | |
0% { transform: translateX(100%);} | |
100% { transform: translateX(0%);} | |
} | |
.carousel-item.prev { | |
animation-name: slideLeftOut; | |
} | |
.carousel-item.active.next { | |
animation-name: slideRightIn; | |
} | |
/* slide("_L") */ | |
@keyframes slideRightOut { | |
0% { transform: translateX(0%);} | |
100% { transform: translateX(100%);} | |
} | |
@keyframes slideLeftIn { | |
0% { transform: translateX(-100%);} | |
100% { transform: translateX(0%);} | |
} | |
.carousel-item.prev.active { | |
animation-name: slideLeftIn; | |
} | |
.carousel-item.next { | |
animation-name: slideRightOut; | |
} | |
/* | |
Carousel UI | |
*/ | |
.carousel-btn, .carousel-dot { | |
background: rgba(0,0,0,0.5); | |
color: red; | |
cursor: pointer; | |
} | |
/* Buttons */ | |
.carousel-btn { | |
position: absolute; | |
top: 0; | |
bottom: 0; | |
width: 3em; | |
outline: 0; | |
border: 0; | |
} | |
.carousel-btn[data-dir="_L"] { | |
left: 0; | |
} | |
.carousel-btn[data-dir="_R"] { | |
right: 0; | |
} | |
.carousel-btn[data-dir="_L"]:after, | |
.carousel-btn[data-dir="_R"]:after { | |
content: ''; | |
width: 2em; | |
height: 2em; | |
position: absolute; | |
margin-top: -1em; | |
transform: rotate(45deg); | |
} | |
.carousel-btn[data-dir="_L"]:after { | |
border-left: 0.5em solid currentColor; | |
border-bottom: 0.5em solid currentColor; | |
left: 1em; | |
} | |
.carousel-btn[data-dir="_R"]:after { | |
border-right: 0.5em solid currentColor; | |
border-top: 0.5em solid currentColor; | |
right: 1em; | |
} | |
/* Dots */ | |
.carousel-dots { | |
position: absolute; | |
bottom: 0; | |
left: 0; | |
right: 0; | |
height: 2em; | |
text-align: center; | |
} | |
.carousel-dot { | |
display: inline-block; | |
width: 1em; | |
height: 1em; | |
border-radius: 50%; | |
margin: 0 0.5em; | |
position: relative; | |
top: 0.5em; | |
transition: all 0.4s ease-out 0s; | |
transform: scale(1); | |
} | |
.carousel-dot.active { | |
background: currentColor; | |
transform: scale(1.2); | |
} |
Testing https://github.com/swieder227/POJS-carousel
A Pen by Andreas Borgen on CodePen.