1
0
mirror of https://github.com/moparisthebest/app-UI synced 2025-01-07 11:48:04 -05:00
app-UI/samples/03 - slidingView/03 - swipeview/swipeview.js

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);