You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

730 lines
26 KiB
JavaScript

/*
* TouchSlider
* @author qiqiboy
* @github https://github.com/qiqiboy/touchslider
*/
;
(function(ROOT, struct, undefined){
"use strict";
var VERSION='2.0.1';
var lastTime=0,
nextFrame=ROOT.requestAnimationFrame ||
ROOT.webkitRequestAnimationFrame ||
ROOT.mozRequestAnimationFrame ||
ROOT.msRequestAnimationFrame ||
function(callback){
var currTime=+new Date,
delay=Math.max(1000/60,1000/60-(currTime-lastTime));
lastTime=currTime+delay;
return setTimeout(callback,delay);
},
cancelFrame=ROOT.cancelAnimationFrame ||
ROOT.webkitCancelAnimationFrame ||
ROOT.webkitCancelRequestAnimationFrame ||
ROOT.mozCancelRequestAnimationFrame ||
ROOT.msCancelRequestAnimationFrame ||
clearTimeout,
DOC=ROOT.document,
divstyle=DOC.createElement('div').style,
cssVendor=function(){
var tests="-webkit- -moz- -o- -ms-".split(" "),
prop;
while(prop=tests.shift()){
if(camelCase(prop+'transform') in divstyle){
return prop;
}
}
return '';
}(),
transition=cssTest('transition'),
toString=Object.prototype.toString,
slice=[].slice,
class2type={},
event2type={},
event2code={
click:4,
mousewheel:5,
dommousescroll:5,
keydown:6,
resize:7
},
POINTERTYPES={
2:'touch',
3:'pen',
4:'mouse',
pen:'pen'
},
STARTEVENT=[],
MOVEEVENT=[],
EVENT=function(){
var ret={},
states={
start:1,
down:1,
move:2,
end:3,
up:3,
cancel:3
};
each("mouse touch pointer MSPointer-".split(" "),function(prefix){
var _prefix=/pointer/i.test(prefix)?'pointer':prefix;
ret[_prefix]=ret[_prefix]||{};
POINTERTYPES[_prefix]=_prefix;
each(states,function(endfix,code){
var ev=camelCase(prefix+endfix);
ret[_prefix][ev]=code;
event2type[ev.toLowerCase()]=_prefix;
event2code[ev.toLowerCase()]=code;
if(code==1){
STARTEVENT.push(ev);
}else{
MOVEEVENT.push(ev);
}
});
});
each("otransitionend oTransitionEnd webkitTransitionEnd mozTransitionEnd MSTransitionEnd transitionend".split(" "),function(ev){
STARTEVENT.push(ev);
event2code[ev.toLowerCase()]=8;
});
return ret;
}(),
POINTERS={
touch:{},
pointer:{},
mouse:{}
};
each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(name){
class2type["[object "+name+"]"]=name.toLowerCase();
});
function type(obj){
if(obj==null){
return obj+"";
}
return typeof obj=='object'||typeof obj=='function' ? class2type[toString.call(obj)]||"object" :
typeof obj;
}
function isArrayLike(elem){
var tp=type(elem);
return !!elem && tp!='function' && tp!='string' && (elem.length===0 || elem.length && (elem.nodeType==1 || (elem.length-1) in elem));
}
function camelCase(str){
return (str+'').replace(/^-ms-/, 'ms-').replace(/-([a-z]|[0-9])/ig, function(all, letter){
return (letter+'').toUpperCase();
});
}
function cssTest(name){
var prop=camelCase(name),
_prop=camelCase(cssVendor+prop);
return (prop in divstyle) && prop || (_prop in divstyle) && _prop || '';
}
function isFunction(func){
return type(func)=='function';
}
function pointerLength(obj){
var len=0,key;
if(type(obj.length)=='number'){
len=obj.length;
}else if('keys' in Object){
len=Object.keys(obj).length;
}else{
for(key in obj){
if(obj.hasOwnProperty(key)){
len++;
}
}
}
return len;
}
function pointerItem(obj,n){
return 'item' in obj?obj.item(n):function(){
var i=0,key;
for(key in this){
if(i++==n){
return this[key];
}
}
}.call(obj,n);
}
function each(arr, iterate){
if(isArrayLike(arr)){
if(type(arr.forEach)=='function'){
return arr.forEach(iterate);
}
var i=0,len=arr.length,item;
for(;i<len;i++){
item=arr[i];
if(type(item)!='undefined'){
iterate(item,i,arr);
}
}
}else{
var key;
for(key in arr){
iterate(key,arr[key],arr);
}
}
}
function children(elem){
var ret=[];
each(elem.children||elem.childNodes,function(elem){
if(elem.nodeType==1){
ret.push(elem);
}
});
return ret;
}
function getStyle(elem,prop){
var style=ROOT.getComputedStyle&&ROOT.getComputedStyle(elem,null)||elem.currentStyle||elem.style;
return style[prop];
}
function setStyle(elem,props){
each(props,function(name,value){
var prop;
switch(name){
case 'float':
prop=cssTest('cssFloat')?'cssFloat':'styleFloat';
break;
default:
prop=camelCase(name);
}
try{
elem.style[prop]=value;
}catch(e){}
});
}
function addListener(elem,evstr,handler){
if(type(evstr)=='object'){
return each(evstr,function(evstr,handler){
addListener(elem,evstr,handler);
});
}
each(evstr.split(" "),function(ev){
if(elem.addEventListener){
elem.addEventListener(ev,handler,false);
}else if(elem.attachEvent){
elem.attachEvent('on'+ev,handler);
}else elem['on'+ev]=handler;
});
}
function offListener(elem,evstr,handler){
if(type(evstr)=='object'){
return each(evstr,function(evstr,handler){
offListener(elem,evstr,handler);
});
}
each(evstr.split(" "),function(ev){
if(elem.removeEventListener){
elem.removeEventListener(ev,handler,false);
}else if(elem.detachEvent){
elem.detachEvent('on'+ev,handler);
}else elem['on'+ev]=null;
});
}
function removeRange(){
var range;
if(ROOT.getSelection){
range=getSelection();
if('empty' in range)range.empty();
else if('removeAllRanges' in range)range.removeAllRanges();
}else{
DOC.selection.empty();
}
}
function EASE(t,b,c,d){
return -c * ((t=t/d-1)*t*t*t - 1) + b;
}
function filterEvent(oldEvent){
var ev={},
which=oldEvent.which,
button=oldEvent.button,
pointers,pointer;
each("wheelDelta detail which keyCode".split(" "),function(prop){
ev[prop]=oldEvent[prop];
});
ev.oldEvent=oldEvent;
ev.type=oldEvent.type.toLowerCase();
ev.eventType=event2type[ev.type]||ev.type;
ev.eventCode=event2code[ev.type]||0;
ev.pointerType=POINTERTYPES[oldEvent.pointerType]||oldEvent.pointerType||ev.eventType;
ev.target=oldEvent.target||oldEvent.srcElement||DOC.documentElement;
if(ev.target.nodeType===3){
ev.target=ev.target.parentNode;
}
ev.preventDefault=function(){
oldEvent.preventDefault && oldEvent.preventDefault();
ev.returnValue=oldEvent.returnValue=false;
}
if(pointers=POINTERS[ev.eventType]){
switch(ev.eventType){
case 'mouse':
case 'pointer':
var id=oldEvent.pointerId||0;
ev.eventCode==3?delete pointers[id]:pointers[id]=oldEvent;
break;
case 'touch':
POINTERS[ev.eventType]=pointers=oldEvent.touches;
break;
}
if(pointer=pointerItem(pointers,0)){
ev.clientX=pointer.clientX;
ev.clientY=pointer.clientY;
}
ev.button=which<4?Math.max(0,which-1):button&4&&1||button&2; // left:0 middle:1 right:2
ev.length=pointerLength(pointers);
}
return ev;
}
struct.prototype={
version:VERSION,
constructor:struct,
latestTime:0,
init:function(config){
var self=this,
handler=this.handler=function(ev){
self.handleEvent(ev);
}
this.events={};
this.duration=isNaN(parseInt(config.duration))?600:parseInt(config.duration);
this.direction=parseInt(config.direction)==0?0:1;
this.current=parseInt(config.start)||0;
this.mouse=config.mouse==null?true:!!config.mouse;
this.mousewheel=!!config.mousewheel;
this.interval=parseInt(config.interval)||5000;
this.playing=config.autoplay==null?true:!!config.autoplay;
this.arrowkey=!!config.arrowkey;
this.fullsize=config.fullsize==null?true:!!config.fullsize;
this.align=config.align||'center';
this.pages=children(this.container);
this.length=this.pages.length;
this.pageData=[];
addListener(this.container,STARTEVENT.join(" ")+" click"+(this.mousewheel?" mousewheel DOMMouseScroll":""),handler);
addListener(DOC,MOVEEVENT.join(" ")+(this.arrowkey?" keydown":""),handler);
addListener(ROOT,'resize',handler);
each(this.pages,function(page){
self.pageData.push({
cssText:page.style.cssText||''
});
});
this.pageData.container=this.container.style.cssText||'';
this.on({
before:function(){clearTimeout(this.playTimer);},
dragStart:function(){clearTimeout(this.playTimer);removeRange();},
after:this.firePlay
}).firePlay();
this.comment=document.createComment(' Powered by TouchSlider v'+this.version+' https://github.com/qiqiboy/touchslider ');
this.container.appendChild(this.comment);
this.resize();
},
resize:function(){
var self=this,
type=this.direction?'height':'width',
pst=getStyle(this.container,'position'),
css;
this.size=this.getSize(this.offsetParent=this.container[pst=='absolute'||pst=='fixed'?'offsetParent':'parentNode']||DOC.body);
css={
float:'left',
display:'inline'
}
each(this.pages,function(page){
if(self.fullsize){
css[type]=self.size-self.getMarginSize(page)-self.getPaddingSize(page)-self.getBorderSize(page)+'px';
}
if(type=='height'){
css['clear']='both';
}
setStyle(page,css);
});
this.total=this.getSum(0,this.length);
css={};
if(pst=='static'){
css={position:'relative'};
}
css[transition]='none';
css[type]=this.total+'px';
css[this.direction?'top':'left']=this.getPos(this.current)+'px';
cancelFrame(this.timer);
setStyle(this.container,css);
return this;
},
on:function(ev,callback){
var self=this;
if(type(ev)=='object'){
each(ev,function(ev,callback){
self.on(ev,callback);
});
}else{
if(!this.events[ev]){
this.events[ev]=[];
}
this.events[ev].push(callback);
}
return this;
},
fire:function(ev){
var self=this,
args=slice.call(arguments,1);
each(this.events[ev]||[],function(func){
if(isFunction(func)){
func.apply(self,args);
}
});
return this;
},
isStatic:function(){
return !this.timer && !this.drag;
},
prev:function(){
return this.slide((this.current-1+this.length)%this.length);
},
next:function(){
return this.slide((this.current+1)%this.length);
},
play:function(){
this.playing=true;
return this.firePlay();
},
firePlay:function(){
var self=this;
if(this.playing){
this.playTimer=setTimeout(function(){
self.next();
},this.interval);
}
return this;
},
pause:function(){
this.playing=false;
clearTimeout(this.playTimer);
return this;
},
slide:function(_index){
var self=this,
dir=this.direction,
stime=+new Date,
duration=this.duration,
current=this.current,
index=Math.min(Math.max(0,_index),this.length-1),
curSize=this.getSum(index,index+1),
curPos=parseFloat(getStyle(this.container,dir?'top':'left'))||0,
type=dir?'top':'left',
css={},tarPos;
tarPos=this.getPos(index);
duration*=Math.min(1,Math.abs(tarPos-curPos)/curSize)||10;
this.current=index;
this.latestTime=stime+duration;
this.fire('before',current,index);
this.end=function(){
delete self.timer;
self.fire('after',index,current);
}
if(transition){
this.timer=1;
css[transition]=type+' '+duration+'ms ease';
css[type]=tarPos+'px';
setStyle(this.container,css);
}else{
cancelFrame(this.timer);
ani();
}
function ani(){
var offset=Math.min(duration,+new Date-stime),
s=EASE(offset,0,1,duration)
cp=(tarPos-curPos)*s+curPos;
self.container.style[type]=cp+'px';
if(offset==duration){
self.end();
}else{
self.timer=nextFrame(ani);
}
}
},
handleEvent:function(oldEvent){
var ev=filterEvent(oldEvent),
canDrag=ev.button<1&&ev.length<2&&(!this.pointerType||this.pointerType==ev.eventType)&&(this.mouse||ev.pointerType!='mouse');
switch(ev.eventCode){
case 2:
if(canDrag&&this.rect){
var index=this.current,
dir=this.direction,
rect=[ev.clientX,ev.clientY],
_rect=this.rect,
offset=rect[dir]-_rect[dir];
if(this.drag==null && _rect.toString()!=rect.toString()){
this.drag=Math.abs(offset)>=Math.abs(rect[1-dir]-_rect[1-dir]);
this.drag && this.fire('dragStart',ev);
}
if(this.drag){
if(!this.pages[index+(offset>0?-1:1)]){
offset/=Math.abs(offset)/this.size+2;
}
this.container.style[dir?'top':'left']=this.startPos+offset+'px';
this.fire('dragMove',ev);
this._offset=offset;
ev.preventDefault();
}
}
break;
case 1:
case 3:
if(canDrag){
var self=this,
index=this.current,
type=this.direction?'top':'left',
isDrag,offset,tm,nn,sub,curPos,tarPos,myWidth;
if(ev.length&&(ev.eventCode==1||this.drag)){
nn=ev.target.nodeName.toLowerCase();
clearTimeout(this.eventTimer);
if(!this.pointerType){
this.pointerType=ev.eventType;
}
this.startPos=parseFloat(getStyle(this.container,type))||0;
if(transition){
this.container.style[transition]='none';
}else if(this.timer){
cancelFrame(this.timer);
delete this.timer;
}
this.rect=[ev.clientX,ev.clientY];
this.time=+new Date;
this.container.style[type]=this.startPos+'px';
if(ev.eventType!='touch' && (nn=='a' || nn=='img')){
ev.preventDefault();
}
}else if(tm=this.time){
offset=this._offset||0;
isDrag=this.drag;
curPos=this.startPos+offset;
tarPos=this.getPos(index);
each("rect drag time startPos _offset".split(" "),function(prop){
delete self[prop];
});
if(isDrag){
sub=offset>0?1:-1;
while(sub*(curPos-tarPos)>this.getOuterSize(this.pages[index])/2&&this.pages[index-sub]){
tarPos=this.getPos(index-=sub);
}
if(Math.abs(offset)>20&&+new Date-tm<500){
index-=sub;
}
this.fire('dragEnd',ev);
ev.preventDefault();
}
if(curPos!=tarPos){
this.slide(index);
}
this.eventTimer=setTimeout(function(){
delete self.pointerType;
},30);
}
}
break;
case 4:
if(this.timer){
ev.preventDefault();
}
break;
case 5:
ev.preventDefault();
if(this.isStatic() && +new Date-this.latestTime>Math.max(1000-this.duration,0)){
var wd=ev.wheelDelta||-ev.detail;
Math.abs(wd)>=3 && this[wd>0?'prev':'next']();
}
break;
case 6:
var nn=ev.target.nodeName.toLowerCase();
if(this.isStatic() && nn!='input' && nn!='textarea' && nn!='select'){
switch(ev.keyCode||ev.which){
case 33:
case 37:
case 38:
this.prev();
break;
case 32:
case 34:
case 39:
case 40:
this.next();
break;
case 35:
this.slide(this.length-1);
break;
case 36:
this.slide(0);
break;
}
}
break;
case 7:
this.resize();
break;
case 8:
if(oldEvent.propertyName==(this.direction?'top':'left')){
this.container.style[transition]='none';
this.end();
}
break;
}
},
getSum:function(from,to){
var sum=0;
while(from<to){
sum+=this.getOuterSize(this.pages[from++],true);
}
return sum;
},
getPos:function(index){
var type=this.direction?'Top':'Left',
myWidth=this.getOuterSize(this.pages[index],true),
sum=this.getSum(0,index)+this['getMargin'+type+'Size'](this.container)+this['getBorder'+type+'Size'](this.container);
switch(this.align){
case 'top':
case 'left':
return -sum;
case 'bottom':
case 'right':
return this.size-myWidth-sum;
default:
return (this.size-myWidth)/2-sum;
}
},
getOuterSize:function(elem,withMargin){
return elem[this.direction?'offsetHeight':'offsetWidth']+(withMargin?this.getMarginSize(elem):0);
},
getInnerSize:function(elem){
return this.getOuterSize(elem)-this.getBorderSize(elem);
},
getSize:function(elem){
return elem[this.direction?'offsetHeight':'offsetWidth']-this.getPaddingSize(elem)-this.getBorderSize(elem);
},
destroy:function(){
var pageData=this.pageData;
offListener(this.container,STARTEVENT.join(" ")+" click"+(this.mousewheel?" mousewheel DOMMouseScroll":""),this.handler);
offListener(DOC,MOVEEVENT.join(" ")+(this.arrowkey?" keydown":""),this.handler);
offListener(ROOT,'resize',this.handler);
each(this.pages,function(page,index){
page.style.cssText=pageData[index].cssText;
});
this.container.style.cssText=pageData.container;
this.container.removeChild(this.comment);
this.length=0;
return this.pause();
},
refresh:function(){
this.pages=children(this.container);
this.length=this.pages.length;
this.current=Math.max(Math.min(this.length-1,this.current),0);
return this.resize();
},
append:function(elem,index){
if(null==index){
index=this.pages.length;
}
this.pageData.splice(index,0,{
cssText:elem.style.cssText||''
});
this.container.insertBefore(elem,this.pages[index]||null);
return this.refresh();
},
prepend:function(elem){
return this.append(elem,0);
},
insertBefore:function(elem,index){
return this.append(elem,index-1);
},
insertAfter:function(elem,index){
return this.append(elem,index+1);
},
remove:function(index){
this.container.removeChild(this.pages[index]);
this.pageData.splice(index,1);
return this.refresh();
}
}
each("margin padding border".split(" "),function(type){
each("Top Left Right Bottom".split(" "),function(dir){
var prop=type+dir;
struct.prototype[camelCase('get-'+prop)+'Size']=function(elem){
return parseFloat(getStyle(elem,prop+(type=='border'?'Width':'')))||0;
}
});
struct.prototype[camelCase('get-'+type)+'Size']=function(elem){
return this[camelCase('get-'+type)+(this.direction?'Top':'Left')+'Size'](elem)+this[camelCase('get-'+type)+(this.direction?'Bottom':'Right')+'Size'](elem);
}
});
ROOT.TouchSlider=struct;
})(window, function(wrap,config){
if(!(this instanceof arguments.callee)){
return new arguments.callee(wrap,config);
}
this.container=typeof wrap=='string'?document.getElementById(wrap):wrap;
this.init(config||{});
});