A Pen by Matthew Daniel Brown on CodePen.
Created
January 4, 2019 22:06
-
-
Save matt-daniel-brown/761ec4334526eac077a2c09340eed299 to your computer and use it in GitHub Desktop.
Ionic Slider View with preview
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div id="js-slides" class="slider"> | |
<div class="slider-slides"> | |
<div class="slide slider-slide">1</div> | |
<div class="slide slider-slide">2</div> | |
<div class="slide slider-slide">3</div> | |
<div class="slide slider-slide">4</div> | |
<div class="slide slider-slide">5</div> | |
</div> | |
</div> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var Slider = { | |
initialize: function (options) { | |
var slider = this; | |
// utilities | |
var noop = function() {}; // simple no operation function | |
var offloadFn = function(fn) { setTimeout(fn || noop, 0); }; // offload a functions execution | |
// check browser capabilities | |
var browser = { | |
addEventListener: !!window.addEventListener, | |
touch: ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch, | |
transitions: (function(temp) { | |
var props = ['transitionProperty', 'WebkitTransition', 'MozTransition', 'OTransition', 'msTransition']; | |
for ( var i in props ) if (temp.style[ props[i] ] !== undefined) return true; | |
return false; | |
})(document.createElement('swipe')) | |
}; | |
var container = options.el; | |
// quit if no root element | |
if (!container) return; | |
var element = container.children[0]; | |
var slides, slidePos, width, length; | |
options = options || {}; | |
var index = parseInt(options.startSlide, 10) || 0; | |
var speed = options.speed || 300; | |
options.continuous = options.continuous !== undefined ? options.continuous : true; | |
function setup() { | |
// do not setup if the container has no width | |
if (!container.offsetWidth) { | |
return; | |
} | |
// cache slides | |
slides = element.children; | |
length = slides.length; | |
// set continuous to false if only one slide | |
if (slides.length < 2) options.continuous = false; | |
//special case if two slides | |
if (browser.transitions && options.continuous && slides.length < 3) { | |
element.appendChild(slides[0].cloneNode(true)); | |
element.appendChild(element.children[1].cloneNode(true)); | |
slides = element.children; | |
} | |
// create an array to store current positions of each slide | |
slidePos = new Array(slides.length); | |
// determine width of each slide | |
width = (container.offsetWidth || container.getBoundingClientRect().width) * 0.75; | |
element.style.width = (slides.length * width) + 'px'; | |
// stack elements | |
var pos = slides.length; | |
while(pos--) { | |
var slide = slides[pos]; | |
slide.style.width = width + 'px'; | |
slide.setAttribute('data-index', pos); | |
if (browser.transitions) { | |
slide.style.left = (pos * -width) + 'px'; | |
move(pos, index > pos ? -width : (index < pos ? width : 0), 0); | |
} | |
} | |
// reposition elements before and after index | |
if (options.continuous && browser.transitions) { | |
move(circle(index - 1), -width, 0); | |
move(circle(index + 1), width, 0); | |
} | |
if (!browser.transitions) element.style.left = (index * -width) + 'px'; | |
container.style.visibility = 'visible'; | |
options.slidesChanged && options.slidesChanged(); | |
} | |
function prev(slideSpeed) { | |
if (options.continuous) slide(index - 1, slideSpeed); | |
else if (index) slide(index - 1, slideSpeed); | |
} | |
function next(slideSpeed) { | |
if (options.continuous) slide(index + 1, slideSpeed); | |
else if (index < slides.length - 1) slide(index + 1, slideSpeed); | |
} | |
function circle(index) { | |
// a simple positive modulo using slides.length | |
return (slides.length + (index % slides.length)) % slides.length; | |
} | |
function slide(to, slideSpeed) { | |
// do nothing if already on requested slide | |
if (index == to) return; | |
if (browser.transitions) { | |
var direction = Math.abs(index - to) / (index - to); // 1: backward, -1: forward | |
// get the actual position of the slide | |
if (options.continuous) { | |
var naturalDirection = direction; | |
direction = -slidePos[circle(to)] / width; | |
// if going forward but to < index, use to = slides.length + to | |
// if going backward but to > index, use to = -slides.length + to | |
if (direction !== naturalDirection) to = -direction * slides.length + to; | |
} | |
var diff = Math.abs(index - to) - 1; | |
// move all the slides between index and to in the right direction | |
while (diff--) move( circle((to > index ? to : index) - diff - 1), width * direction, 0); | |
to = circle(to); | |
move(index, width * direction, slideSpeed || speed); | |
move(to, 0, slideSpeed || speed); | |
if (options.continuous) move(circle(to - direction), -(width * direction), 0); // we need to get the next in place | |
} else { | |
to = circle(to); | |
animate(index * -width, to * -width, slideSpeed || speed); | |
//no fallback for a circular continuous if the browser does not accept transitions | |
} | |
index = to; | |
offloadFn(options.callback && options.callback(index, slides[index])); | |
} | |
function move(index, dist, speed, z) { | |
translate(index, dist, speed, z || 0); | |
slidePos[index] = dist; | |
} | |
function translate(index, dist, speed, z) { | |
var slide = slides[index]; | |
var style = slide && slide.style; | |
if (!style) return; | |
style.webkitTransitionDuration = | |
style.MozTransitionDuration = | |
style.msTransitionDuration = | |
style.OTransitionDuration = | |
style.transitionDuration = speed + 'ms'; | |
style.webkitTransform = 'translate3d(' + dist + 'px, 0 ,0)'; | |
// style.msTransform = | |
// style.MozTransform = | |
// style.OTransform = 'translate3d(' + dist + 'px, 0, ' + z + 'px)'; | |
} | |
function animate(from, to, speed) { | |
// if not an animation, just reposition | |
if (!speed) { | |
element.style.left = to + 'px'; | |
return; | |
} | |
var start = +new Date(); | |
var timer = setInterval(function() { | |
var timeElap = +new Date() - start; | |
if (timeElap > speed) { | |
element.style.left = to + 'px'; | |
if (delay) begin(); | |
options.transitionEnd && options.transitionEnd.call(event, index, slides[index]); | |
clearInterval(timer); | |
return; | |
} | |
element.style.left = (( (to - from) * (Math.floor((timeElap / speed) * 100) / 100) ) + from) + 'px'; | |
}, 4); | |
} | |
// setup auto slideshow | |
var delay = options.auto || 0; | |
var interval; | |
function begin() { | |
interval = setTimeout(next, delay); | |
} | |
function stop() { | |
delay = options.auto || 0; | |
clearTimeout(interval); | |
} | |
// setup initial vars | |
var start = {}; | |
var delta = {}; | |
var isScrolling; | |
// setup event capturing | |
var events = { | |
handleEvent: function(event) { | |
if(event.type == 'mousedown' || event.type == 'mouseup' || event.type == 'mousemove') { | |
event.touches = [{ | |
pageX: event.pageX, | |
pageY: event.pageY | |
}]; | |
} | |
switch (event.type) { | |
case 'mousedown': this.start(event); break; | |
case 'touchstart': this.start(event); break; | |
case 'touchmove': this.touchmove(event); break; | |
case 'mousemove': this.touchmove(event); break; | |
case 'touchend': offloadFn(this.end(event)); break; | |
case 'mouseup': offloadFn(this.end(event)); break; | |
case 'webkitTransitionEnd': | |
case 'msTransitionEnd': | |
case 'oTransitionEnd': | |
case 'otransitionend': | |
case 'transitionend': offloadFn(this.transitionEnd(event)); break; | |
case 'resize': offloadFn(setup); break; | |
} | |
if (options.stopPropagation) event.stopPropagation(); | |
}, | |
start: function(event) { | |
var touches = event.touches[0]; | |
// measure start values | |
start = { | |
// get initial touch coords | |
x: touches.pageX, | |
y: touches.pageY, | |
// store time to determine touch duration | |
time: +new Date() | |
}; | |
// used for testing first move event | |
isScrolling = undefined; | |
// reset delta and end measurements | |
delta = {}; | |
// attach touchmove and touchend listeners | |
if(browser.touch) { | |
element.addEventListener('touchmove', this, false); | |
element.addEventListener('touchend', this, false); | |
} else { | |
element.addEventListener('mousemove', this, false); | |
element.addEventListener('mouseup', this, false); | |
document.addEventListener('mouseup', this, false); | |
} | |
}, | |
touchmove: function(event) { | |
// ensure swiping with one touch and not pinching | |
// ensure sliding is enabled | |
if (event.touches.length > 1 || | |
event.scale && event.scale !== 1 || | |
slider.slideIsDisabled) { | |
return; | |
} | |
if (options.disableScroll) event.preventDefault(); | |
var touches = event.touches[0]; | |
// measure change in x and y | |
delta = { | |
x: touches.pageX - start.x, | |
y: touches.pageY - start.y | |
}; | |
// determine if scrolling test has run - one time test | |
if ( typeof isScrolling == 'undefined') { | |
isScrolling = !!( isScrolling || Math.abs(delta.x) < Math.abs(delta.y) ); | |
} | |
// if user is not trying to scroll vertically | |
if (!isScrolling) { | |
// prevent native scrolling | |
event.preventDefault(); | |
// stop slideshow | |
stop(); | |
// increase resistance if first or last slide | |
if (options.continuous) { // we don't add resistance at the end | |
translate(circle(index - 1), delta.x + slidePos[circle(index - 1)], 0); | |
translate(index, delta.x + slidePos[index], 0); | |
translate(circle(index + 1), delta.x + slidePos[circle(index + 1)], 0); | |
} else { | |
delta.x = | |
delta.x / | |
( (!index && delta.x > 0 || // if first slide and sliding left | |
index == slides.length - 1 && // or if last slide and sliding right | |
delta.x < 0 // and if sliding at all | |
) ? | |
( Math.abs(delta.x) / width + 1 ) // determine resistance level | |
: 1 ); // no resistance if false | |
// translate 1:1 | |
translate(index - 1, delta.x + slidePos[index - 1], 0); | |
translate(index, delta.x + slidePos[index], 0); | |
translate(index + 1, delta.x + slidePos[index + 1], 0); | |
} | |
options.onDrag && options.onDrag(); | |
} | |
}, | |
end: function() { | |
// measure duration | |
var duration = +new Date() - start.time; | |
// determine if slide attempt triggers next/prev slide | |
var isValidSlide = | |
Number(duration) < 250 && // if slide duration is less than 250ms | |
Math.abs(delta.x) > 20 || // and if slide amt is greater than 20px | |
Math.abs(delta.x) > width / 2; // or if slide amt is greater than half the width | |
// determine if slide attempt is past start and end | |
var isPastBounds = (!index && delta.x > 0) || // if first slide and slide amt is greater than 0 | |
(index == slides.length - 1 && delta.x < 0); // or if last slide and slide amt is less than 0 | |
if (options.continuous) isPastBounds = false; | |
// determine direction of swipe (true:right, false:left) | |
var direction = delta.x < 0; | |
// if not scrolling vertically | |
if (!isScrolling) { | |
if (isValidSlide && !isPastBounds) { | |
if (direction) { | |
if (options.continuous) { // we need to get the next in this direction in place | |
move(circle(index - 1), -width, 0); | |
move(circle(index + 2), width, 0); | |
} else { | |
move(index - 1, -width, 0); | |
} | |
move(index, slidePos[index] - width, speed); | |
move(circle(index + 1), slidePos[circle(index + 1)] - width, speed); | |
index = circle(index + 1); | |
} else { | |
if (options.continuous) { // we need to get the next in this direction in place | |
move(circle(index + 1), width, 0, -1); | |
move(circle(index - 2), -width, 0, -1); | |
} else { | |
move(index + 1, width, 0); | |
} | |
move(index, slidePos[index] + width, speed); | |
move(circle(index - 1), slidePos[circle(index - 1)] + width, speed); | |
index = circle(index - 1); | |
} | |
options.callback && options.callback(index, slides[index]); | |
} else { | |
if (options.continuous) { | |
move(circle(index - 1), -width, speed); | |
move(index, 0, speed); | |
move(circle(index + 1), width, speed); | |
} else { | |
move(index - 1, -width, speed); | |
move(index, 0, speed); | |
move(index + 1, width, speed); | |
} | |
} | |
} | |
// kill touchmove and touchend event listeners until touchstart called again | |
if(browser.touch) { | |
element.removeEventListener('touchmove', events, false); | |
element.removeEventListener('touchend', events, false); | |
} else { | |
element.removeEventListener('mousemove', events, false); | |
element.removeEventListener('mouseup', events, false); | |
document.removeEventListener('mouseup', events, false); | |
} | |
options.onDragEnd && options.onDragEnd(); | |
}, | |
transitionEnd: function(event) { | |
if (parseInt(event.target.getAttribute('data-index'), 10) == index) { | |
if (delay) begin(); | |
options.transitionEnd && options.transitionEnd.call(event, index, slides[index]); | |
} | |
} | |
}; | |
// Public API | |
this.update = function() { | |
setTimeout(setup); | |
}; | |
this.setup = function() { | |
setup(); | |
}; | |
this.loop = function(value) { | |
if (arguments.length) options.continuous = !!value; | |
return options.continuous; | |
}; | |
this.enableSlide = function(shouldEnable) { | |
if (arguments.length) { | |
this.slideIsDisabled = !shouldEnable; | |
} | |
return !this.slideIsDisabled; | |
}; | |
this.slide = this.select = function(to, speed) { | |
// cancel slideshow | |
stop(); | |
slide(to, speed); | |
}; | |
this.prev = this.previous = function() { | |
// cancel slideshow | |
stop(); | |
prev(); | |
}; | |
this.next = function() { | |
// cancel slideshow | |
stop(); | |
next(); | |
}; | |
this.stop = function() { | |
// cancel slideshow | |
stop(); | |
}; | |
this.start = function() { | |
begin(); | |
}; | |
this.autoPlay = function(newDelay) { | |
if (!delay || delay < 0) { | |
stop(); | |
} else { | |
delay = newDelay; | |
begin(); | |
} | |
}; | |
this.currentIndex = this.selected = function() { | |
// return current index position | |
return index; | |
}; | |
this.slidesCount = this.count = function() { | |
// return total number of slides | |
return length; | |
}; | |
this.kill = function() { | |
// cancel slideshow | |
stop(); | |
// reset element | |
element.style.width = ''; | |
element.style.left = ''; | |
// reset slides so no refs are held on to | |
slides && (slides.length = []); | |
// removed event listeners | |
if (browser.addEventListener) { | |
// remove current event listeners | |
element.removeEventListener('touchstart', events, false); | |
element.removeEventListener('webkitTransitionEnd', events, false); | |
element.removeEventListener('msTransitionEnd', events, false); | |
element.removeEventListener('oTransitionEnd', events, false); | |
element.removeEventListener('otransitionend', events, false); | |
element.removeEventListener('transitionend', events, false); | |
window.removeEventListener('resize', events, false); | |
} | |
else { | |
window.onresize = null; | |
} | |
}; | |
this.load = function() { | |
// trigger setup | |
setup(); | |
// start auto slideshow if applicable | |
if (delay) begin(); | |
// add event listeners | |
if (browser.addEventListener) { | |
// set touchstart event on element | |
if (browser.touch) { | |
element.addEventListener('touchstart', events, false); | |
} else { | |
element.addEventListener('mousedown', events, false); | |
} | |
if (browser.transitions) { | |
element.addEventListener('webkitTransitionEnd', events, false); | |
element.addEventListener('msTransitionEnd', events, false); | |
element.addEventListener('oTransitionEnd', events, false); | |
element.addEventListener('otransitionend', events, false); | |
element.addEventListener('transitionend', events, false); | |
} | |
// set resize event on window | |
window.addEventListener('resize', events, false); | |
} else { | |
window.onresize = function () { setup(); }; // to play nice with old IE | |
} | |
}; | |
} | |
}; | |
window.onload = function() { | |
var sliderEl = document.getElementById('js-slides'); | |
Slider.initialize({ | |
el: sliderEl, | |
continuous: true | |
}); | |
Slider.load(); | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@import "bourbon"; | |
html, | |
body { | |
width: 100%; | |
height: 100%; | |
margin: 0; | |
} | |
.slider { | |
position: relative; | |
overflow: hidden; | |
height: 100%; | |
} | |
.slider-slides { | |
position: relative; | |
height: 100%; | |
padding: 0 12.5%; | |
} | |
.slider-slide { | |
line-height: 100vh; | |
text-align: center; | |
font-weight: bold; | |
font-size: 8rem; | |
position: relative; | |
display: block; | |
float: left; | |
width: 100%; | |
height: 100%; | |
vertical-align: top; | |
} | |
.slide { | |
height: 100%; | |
} | |
.slide:nth-child(1) { background-color: #F7E1DF; color: #444; } | |
.slide:nth-child(2) { background-color: #FF9418; color: #fff; } | |
.slide:nth-child(3) { background-color: #EF5352; color: #fff; } | |
.slide:nth-child(4) { background-color: #FFFF1C; color: #444; } | |
.slide:nth-child(5) { background-color: #FF177C; color: #fff; } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment