mirror of
https://github.com/moparisthebest/app-UI
synced 2025-01-07 11:48:04 -05:00
474 lines
15 KiB
JavaScript
Executable File
474 lines
15 KiB
JavaScript
Executable File
/*!
|
|
* SwipeView v1.0 ~ Copyright (c) 2012 Matteo Spinelli, http://cubiq.org
|
|
* Released under MIT license, http://cubiq.org/license
|
|
*/
|
|
var SwipeView = (function (window, document) {
|
|
var dummyStyle = document.createElement('div').style,
|
|
vendor = (function () {
|
|
var vendors = 't,webkitT,MozT,msT,OT'.split(','),
|
|
t,
|
|
i = 0,
|
|
l = vendors.length;
|
|
|
|
for ( ; i < l; i++ ) {
|
|
t = vendors[i] + 'ransform';
|
|
if ( t in dummyStyle ) {
|
|
return vendors[i].substr(0, vendors[i].length - 1);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
})(),
|
|
cssVendor = vendor ? '-' + vendor.toLowerCase() + '-' : '',
|
|
|
|
// Style properties
|
|
transform = prefixStyle('transform'),
|
|
transitionDuration = prefixStyle('transitionDuration'),
|
|
|
|
// Browser capabilities
|
|
has3d = prefixStyle('perspective') in dummyStyle,
|
|
hasTouch = 'ontouchstart' in window,
|
|
hasTransform = !!vendor,
|
|
hasTransitionEnd = prefixStyle('transition') in dummyStyle,
|
|
|
|
// Helpers
|
|
translateZ = has3d ? ' translateZ(0)' : '',
|
|
|
|
// Events
|
|
resizeEvent = 'onorientationchange' in window ? 'orientationchange' : 'resize',
|
|
startEvent = hasTouch ? 'touchstart' : 'mousedown',
|
|
moveEvent = hasTouch ? 'touchmove' : 'mousemove',
|
|
endEvent = hasTouch ? 'touchend' : 'mouseup',
|
|
cancelEvent = hasTouch ? 'touchcancel' : 'mouseup',
|
|
transitionEndEvent = (function () {
|
|
if ( vendor === false ) return false;
|
|
|
|
var transitionEnd = {
|
|
'' : 'transitionend',
|
|
'webkit' : 'webkitTransitionEnd',
|
|
'Moz' : 'transitionend',
|
|
'O' : 'oTransitionEnd',
|
|
'ms' : 'MSTransitionEnd'
|
|
};
|
|
|
|
return transitionEnd[vendor];
|
|
})(),
|
|
|
|
SwipeView = function (el, options) {
|
|
var i,
|
|
div,
|
|
className,
|
|
pageIndex;
|
|
|
|
this.wrapper = typeof el == 'string' ? document.querySelector(el) : el;
|
|
this.options = {
|
|
text: null,
|
|
numberOfPages: 3,
|
|
snapThreshold: null,
|
|
hastyPageFlip: false,
|
|
loop: true
|
|
};
|
|
|
|
// User defined options
|
|
for (i in options) this.options[i] = options[i];
|
|
|
|
this.wrapper.style.overflow = 'hidden';
|
|
this.wrapper.style.position = 'relative';
|
|
|
|
this.masterPages = [];
|
|
|
|
div = document.createElement('div');
|
|
div.id = 'swipeview-slider';
|
|
div.style.cssText = 'position:relative;top:0;height:100%;width:100%;' + cssVendor + 'transition-duration:0;' + cssVendor + 'transform:translateZ(0);' + cssVendor + 'transition-timing-function:ease-out';
|
|
this.wrapper.appendChild(div);
|
|
this.slider = div;
|
|
|
|
this.refreshSize();
|
|
|
|
for (i=-1; i<2; i++) {
|
|
div = document.createElement('div');
|
|
div.id = 'swipeview-masterpage-' + (i+1);
|
|
div.style.cssText = cssVendor + 'transform:translateZ(0);position:absolute;top:0;height:100%;width:100%;left:' + i*100 + '%';
|
|
if (!div.dataset) div.dataset = {};
|
|
pageIndex = i == -1 ? this.options.numberOfPages - 1 : i;
|
|
div.dataset.pageIndex = pageIndex;
|
|
div.dataset.upcomingPageIndex = pageIndex;
|
|
|
|
if (!this.options.loop && i == -1) div.style.visibility = 'hidden';
|
|
|
|
this.slider.appendChild(div);
|
|
this.masterPages.push(div);
|
|
}
|
|
|
|
className = this.masterPages[1].className;
|
|
this.masterPages[1].className = !className ? 'swipeview-active' : className + ' swipeview-active';
|
|
|
|
window.addEventListener(resizeEvent, this, false);
|
|
this.wrapper.addEventListener(startEvent, this, false);
|
|
this.wrapper.addEventListener(moveEvent, this, false);
|
|
this.wrapper.addEventListener(endEvent, this, false);
|
|
this.slider.addEventListener(transitionEndEvent, this, false);
|
|
// in Opera >= 12 the transitionend event is lowercase so we register both events
|
|
if ( vendor == 'O' ) this.slider.addEventListener(transitionEndEvent.toLowerCase(), this, false);
|
|
|
|
/* if (!hasTouch) {
|
|
this.wrapper.addEventListener('mouseout', this, false);
|
|
}*/
|
|
};
|
|
|
|
SwipeView.prototype = {
|
|
currentMasterPage: 1,
|
|
x: 0,
|
|
page: 0,
|
|
pageIndex: 0,
|
|
customEvents: [],
|
|
|
|
onFlip: function (fn) {
|
|
this.wrapper.addEventListener('swipeview-flip', fn, false);
|
|
this.customEvents.push(['flip', fn]);
|
|
},
|
|
|
|
onMoveOut: function (fn) {
|
|
this.wrapper.addEventListener('swipeview-moveout', fn, false);
|
|
this.customEvents.push(['moveout', fn]);
|
|
},
|
|
|
|
onMoveIn: function (fn) {
|
|
this.wrapper.addEventListener('swipeview-movein', fn, false);
|
|
this.customEvents.push(['movein', fn]);
|
|
},
|
|
|
|
onTouchStart: function (fn) {
|
|
this.wrapper.addEventListener('swipeview-touchstart', fn, false);
|
|
this.customEvents.push(['touchstart', fn]);
|
|
},
|
|
|
|
destroy: function () {
|
|
while ( this.customEvents.length ) {
|
|
this.wrapper.removeEventListener('swipeview-' + this.customEvents[0][0], this.customEvents[0][1], false);
|
|
this.customEvents.shift();
|
|
}
|
|
|
|
// Remove the event listeners
|
|
window.removeEventListener(resizeEvent, this, false);
|
|
this.wrapper.removeEventListener(startEvent, this, false);
|
|
this.wrapper.removeEventListener(moveEvent, this, false);
|
|
this.wrapper.removeEventListener(endEvent, this, false);
|
|
this.slider.removeEventListener(transitionEndEvent, this, false);
|
|
|
|
/* if (!hasTouch) {
|
|
this.wrapper.removeEventListener('mouseout', this, false);
|
|
}*/
|
|
},
|
|
|
|
refreshSize: function () {
|
|
this.wrapperWidth = this.wrapper.clientWidth;
|
|
this.wrapperHeight = this.wrapper.clientHeight;
|
|
this.pageWidth = this.wrapperWidth;
|
|
this.maxX = -this.options.numberOfPages * this.pageWidth + this.wrapperWidth;
|
|
this.snapThreshold = this.options.snapThreshold === null ?
|
|
Math.round(this.pageWidth * 0.15) :
|
|
/%/.test(this.options.snapThreshold) ?
|
|
Math.round(this.pageWidth * this.options.snapThreshold.replace('%', '') / 100) :
|
|
this.options.snapThreshold;
|
|
},
|
|
|
|
updatePageCount: function (n) {
|
|
this.options.numberOfPages = n;
|
|
this.maxX = -this.options.numberOfPages * this.pageWidth + this.wrapperWidth;
|
|
},
|
|
|
|
goToPage: function (p) {
|
|
var i;
|
|
|
|
this.masterPages[this.currentMasterPage].className = this.masterPages[this.currentMasterPage].className.replace(/(^|\s)swipeview-active(\s|$)/, '');
|
|
for (i=0; i<3; i++) {
|
|
className = this.masterPages[i].className;
|
|
/(^|\s)swipeview-loading(\s|$)/.test(className) || (this.masterPages[i].className = !className ? 'swipeview-loading' : className + ' swipeview-loading');
|
|
}
|
|
|
|
p = p < 0 ? 0 : p > this.options.numberOfPages-1 ? this.options.numberOfPages-1 : p;
|
|
this.page = p;
|
|
this.pageIndex = p;
|
|
this.slider.style[transitionDuration] = '0s';
|
|
this.__pos(-p * this.pageWidth);
|
|
|
|
this.currentMasterPage = (this.page + 1) - Math.floor((this.page + 1) / 3) * 3;
|
|
|
|
this.masterPages[this.currentMasterPage].className = this.masterPages[this.currentMasterPage].className + ' swipeview-active';
|
|
|
|
if (this.currentMasterPage === 0) {
|
|
this.masterPages[2].style.left = this.page * 100 - 100 + '%';
|
|
this.masterPages[0].style.left = this.page * 100 + '%';
|
|
this.masterPages[1].style.left = this.page * 100 + 100 + '%';
|
|
|
|
this.masterPages[2].dataset.upcomingPageIndex = this.page === 0 ? this.options.numberOfPages-1 : this.page - 1;
|
|
this.masterPages[0].dataset.upcomingPageIndex = this.page;
|
|
this.masterPages[1].dataset.upcomingPageIndex = this.page == this.options.numberOfPages-1 ? 0 : this.page + 1;
|
|
} else if (this.currentMasterPage == 1) {
|
|
this.masterPages[0].style.left = this.page * 100 - 100 + '%';
|
|
this.masterPages[1].style.left = this.page * 100 + '%';
|
|
this.masterPages[2].style.left = this.page * 100 + 100 + '%';
|
|
|
|
this.masterPages[0].dataset.upcomingPageIndex = this.page === 0 ? this.options.numberOfPages-1 : this.page - 1;
|
|
this.masterPages[1].dataset.upcomingPageIndex = this.page;
|
|
this.masterPages[2].dataset.upcomingPageIndex = this.page == this.options.numberOfPages-1 ? 0 : this.page + 1;
|
|
} else {
|
|
this.masterPages[1].style.left = this.page * 100 - 100 + '%';
|
|
this.masterPages[2].style.left = this.page * 100 + '%';
|
|
this.masterPages[0].style.left = this.page * 100 + 100 + '%';
|
|
|
|
this.masterPages[1].dataset.upcomingPageIndex = this.page === 0 ? this.options.numberOfPages-1 : this.page - 1;
|
|
this.masterPages[2].dataset.upcomingPageIndex = this.page;
|
|
this.masterPages[0].dataset.upcomingPageIndex = this.page == this.options.numberOfPages-1 ? 0 : this.page + 1;
|
|
}
|
|
|
|
this.__flip();
|
|
},
|
|
|
|
next: function () {
|
|
if (!this.options.loop && this.x == this.maxX) return;
|
|
|
|
this.directionX = -1;
|
|
this.x -= 1;
|
|
this.__checkPosition();
|
|
},
|
|
|
|
prev: function () {
|
|
if (!this.options.loop && this.x === 0) return;
|
|
|
|
this.directionX = 1;
|
|
this.x += 1;
|
|
this.__checkPosition();
|
|
},
|
|
|
|
handleEvent: function (e) {
|
|
switch (e.type) {
|
|
case startEvent:
|
|
this.__start(e);
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
return false;
|
|
break;
|
|
case moveEvent:
|
|
this.__move(e);
|
|
break;
|
|
case cancelEvent:
|
|
case endEvent:
|
|
this.__end(e);
|
|
break;
|
|
case resizeEvent:
|
|
this.__resize();
|
|
break;
|
|
case transitionEndEvent:
|
|
case 'otransitionend':
|
|
if (e.target == this.slider && !this.options.hastyPageFlip) this.__flip();
|
|
break;
|
|
}
|
|
},
|
|
|
|
|
|
/**
|
|
*
|
|
* Pseudo private methods
|
|
*
|
|
*/
|
|
__pos: function (x) {
|
|
this.x = x;
|
|
this.slider.style[transform] = 'translate(' + x + 'px,0)' + translateZ;
|
|
},
|
|
|
|
__resize: function () {
|
|
this.refreshSize();
|
|
this.slider.style[transitionDuration] = '0s';
|
|
this.__pos(-this.page * this.pageWidth);
|
|
},
|
|
|
|
__start: function (e) {
|
|
//e.preventDefault();
|
|
|
|
if (this.initiated) return;
|
|
|
|
var point = hasTouch ? e.touches[0] : e;
|
|
|
|
this.initiated = true;
|
|
this.moved = false;
|
|
this.thresholdExceeded = false;
|
|
this.startX = point.pageX;
|
|
this.startY = point.pageY;
|
|
this.pointX = point.pageX;
|
|
this.pointY = point.pageY;
|
|
this.stepsX = 0;
|
|
this.stepsY = 0;
|
|
this.directionX = 0;
|
|
this.directionLocked = false;
|
|
|
|
/* var matrix = getComputedStyle(this.slider, null).webkitTransform.replace(/[^0-9-.,]/g, '').split(',');
|
|
this.x = matrix[4] * 1;*/
|
|
|
|
this.slider.style[transitionDuration] = '0s';
|
|
|
|
this.__event('touchstart');
|
|
},
|
|
|
|
__move: function (e) {
|
|
if (!this.initiated) return;
|
|
|
|
var point = hasTouch ? e.touches[0] : e,
|
|
deltaX = point.pageX - this.pointX,
|
|
deltaY = point.pageY - this.pointY,
|
|
newX = this.x + deltaX,
|
|
dist = Math.abs(point.pageX - this.startX);
|
|
|
|
this.moved = true;
|
|
this.pointX = point.pageX;
|
|
this.pointY = point.pageY;
|
|
this.directionX = deltaX > 0 ? 1 : deltaX < 0 ? -1 : 0;
|
|
this.stepsX += Math.abs(deltaX);
|
|
this.stepsY += Math.abs(deltaY);
|
|
|
|
// We take a 10px buffer to figure out the direction of the swipe
|
|
if (this.stepsX < 10 && this.stepsY < 10) {
|
|
// e.preventDefault();
|
|
return;
|
|
}
|
|
|
|
// We are scrolling vertically, so skip SwipeView and give the control back to the browser
|
|
if (!this.directionLocked && this.stepsY > this.stepsX) {
|
|
this.initiated = false;
|
|
return;
|
|
}
|
|
|
|
e.preventDefault();
|
|
|
|
this.directionLocked = true;
|
|
|
|
if (!this.options.loop && (newX > 0 || newX < this.maxX)) {
|
|
newX = this.x + (deltaX / 2);
|
|
}
|
|
|
|
if (!this.thresholdExceeded && dist >= this.snapThreshold) {
|
|
this.thresholdExceeded = true;
|
|
this.__event('moveout');
|
|
} else if (this.thresholdExceeded && dist < this.snapThreshold) {
|
|
this.thresholdExceeded = false;
|
|
this.__event('movein');
|
|
}
|
|
|
|
/* if (newX > 0 || newX < this.maxX) {
|
|
newX = this.x + (deltaX / 2);
|
|
}*/
|
|
|
|
this.__pos(newX);
|
|
},
|
|
|
|
__end: function (e) {
|
|
if (!this.initiated) return;
|
|
|
|
var point = hasTouch ? e.changedTouches[0] : e,
|
|
dist = Math.abs(point.pageX - this.startX);
|
|
|
|
this.initiated = false;
|
|
|
|
if (!this.moved) return;
|
|
|
|
if (!this.options.loop && (this.x > 0 || this.x < this.maxX)) {
|
|
dist = 0;
|
|
this.__event('movein');
|
|
}
|
|
|
|
// Check if we exceeded the snap threshold
|
|
if (dist < this.snapThreshold) {
|
|
this.slider.style[transitionDuration] = Math.floor(300 * dist / this.snapThreshold) + 'ms';
|
|
this.__pos(-this.page * this.pageWidth);
|
|
return;
|
|
}
|
|
|
|
this.__checkPosition();
|
|
},
|
|
|
|
__checkPosition: function () {
|
|
var pageFlip,
|
|
pageFlipIndex,
|
|
className;
|
|
|
|
this.masterPages[this.currentMasterPage].className = this.masterPages[this.currentMasterPage].className.replace(/(^|\s)swipeview-active(\s|$)/, '');
|
|
|
|
// Flip the page
|
|
if (this.directionX > 0) {
|
|
this.page = -Math.ceil(this.x / this.pageWidth);
|
|
this.currentMasterPage = (this.page + 1) - Math.floor((this.page + 1) / 3) * 3;
|
|
this.pageIndex = this.pageIndex === 0 ? this.options.numberOfPages - 1 : this.pageIndex - 1;
|
|
|
|
pageFlip = this.currentMasterPage - 1;
|
|
pageFlip = pageFlip < 0 ? 2 : pageFlip;
|
|
this.masterPages[pageFlip].style.left = this.page * 100 - 100 + '%';
|
|
|
|
pageFlipIndex = this.page - 1;
|
|
} else {
|
|
this.page = -Math.floor(this.x / this.pageWidth);
|
|
this.currentMasterPage = (this.page + 1) - Math.floor((this.page + 1) / 3) * 3;
|
|
this.pageIndex = this.pageIndex == this.options.numberOfPages - 1 ? 0 : this.pageIndex + 1;
|
|
|
|
pageFlip = this.currentMasterPage + 1;
|
|
pageFlip = pageFlip > 2 ? 0 : pageFlip;
|
|
this.masterPages[pageFlip].style.left = this.page * 100 + 100 + '%';
|
|
|
|
pageFlipIndex = this.page + 1;
|
|
}
|
|
|
|
// Add active class to current page
|
|
className = this.masterPages[this.currentMasterPage].className;
|
|
/(^|\s)swipeview-active(\s|$)/.test(className) || (this.masterPages[this.currentMasterPage].className = !className ? 'swipeview-active' : className + ' swipeview-active');
|
|
|
|
// Add loading class to flipped page
|
|
className = this.masterPages[pageFlip].className;
|
|
/(^|\s)swipeview-loading(\s|$)/.test(className) || (this.masterPages[pageFlip].className = !className ? 'swipeview-loading' : className + ' swipeview-loading');
|
|
|
|
pageFlipIndex = pageFlipIndex - Math.floor(pageFlipIndex / this.options.numberOfPages) * this.options.numberOfPages;
|
|
this.masterPages[pageFlip].dataset.upcomingPageIndex = pageFlipIndex; // Index to be loaded in the newly flipped page
|
|
|
|
newX = -this.page * this.pageWidth;
|
|
|
|
this.slider.style[transitionDuration] = Math.floor(500 * Math.abs(this.x - newX) / this.pageWidth) + 'ms';
|
|
|
|
// Hide the next page if we decided to disable looping
|
|
if (!this.options.loop) {
|
|
this.masterPages[pageFlip].style.visibility = newX === 0 || newX == this.maxX ? 'hidden' : '';
|
|
}
|
|
|
|
if (this.x == newX) {
|
|
this.__flip(); // If we swiped all the way long to the next page (extremely rare but still)
|
|
} else {
|
|
this.__pos(newX);
|
|
if (this.options.hastyPageFlip) this.__flip();
|
|
}
|
|
},
|
|
|
|
__flip: function () {
|
|
this.__event('flip');
|
|
|
|
for (var i=0; i<3; i++) {
|
|
this.masterPages[i].className = this.masterPages[i].className.replace(/(^|\s)swipeview-loading(\s|$)/, ''); // Remove the loading class
|
|
this.masterPages[i].dataset.pageIndex = this.masterPages[i].dataset.upcomingPageIndex;
|
|
}
|
|
},
|
|
|
|
__event: function (type) {
|
|
var ev = document.createEvent("Event");
|
|
|
|
ev.initEvent('swipeview-' + type, true, true);
|
|
|
|
this.wrapper.dispatchEvent(ev);
|
|
}
|
|
};
|
|
|
|
function prefixStyle (style) {
|
|
if ( vendor === '' ) return style;
|
|
|
|
style = style.charAt(0).toUpperCase() + style.substr(1);
|
|
return vendor + style;
|
|
}
|
|
|
|
return SwipeView;
|
|
})(window, document); |