var scroll_min_step = 8; var scroll_start_step = 20; //触摸后,移动距离超过step后才开始滚动 var scroll_side_width = 30; //边距 //滚动条,android 3.0以下overflow:auto无效 //在滚动事件: onscrolling=function(x, y, isleft, isright, istop, isbottom, touchend, nottouch) //滚动结束事件: onafterscroll=function(x, y, isleft, isright, istop, isbottom, touchend) //从边框开始滚动事件: onscrollingfromside=function(x, y, startx, starty, isleft, isright, istop, isbottom),如果有做处理返回true //从边框开始滚动结束事件: onafterscrollfromside=function(x, y, startx, starty, isleft, isright, istop, isbottom) //参数inertiadisabled: 是否禁止惯性滚动 scroller = function(pageelement, horizontalonly, verticalonly, showscrollbar, alwaysshowscrollbar, twoway, inertiadisabled) { var scollerdivision; if(pageelement.tagname=='body' && (scollerdivision=pageelement.ownerdocument.getelementbyid('scollerdivision'))) { pageelement = scollerdivision; this._resetscrollerelement(scollerdivision); } else if(pageelement.id=='contentarea') { pageelement.style.display = 'none'; this._resetscrollerelement(pageelement); pageelement.style.display = ''; } pageelement.scroller = this; this.pageelement = pageelement; this.horizontalonly = horizontalonly; this.verticalonly = verticalonly; this.showscrollbar = showscrollbar; this.alwaysshowscrollbar = alwaysshowscrollbar; this.twoway = twoway; this.inertiadisabled = inertiadisabled; this.parentscroller = null; for(var obj = this.pageelement; obj; obj = obj.parentnode) { if(obj!=this.pageelement && obj.scroller) { this.parentscroller = obj.scroller; break; } if(obj.tagname=='body' && !(obj=(obj.ownerdocument.parentwindow ? obj.ownerdocument.parentwindow : obj.ownerdocument.defaultview).frameelement)) { break; } } var scroller = this; this.pageelement.ontouchstart = function(event) { event.preventdefault(); return scroller._ontouchstart(event); }; this.pageelement.ontouchmove = function(event) { event.preventdefault(); return scroller._ontouchmove(event); }; this.pageelement.ontouchend = function(event) { event.preventdefault(); return scroller._ontouchend(event); }; this.pageelement.ondragstart = function(event) { event.preventdefault(); return false; }; if(alwaysshowscrollbar) { var pageelementsize = {clientwidth:-1, clientheight:-1, scrollwidth:-1, scrollheight:-1}; window.setinterval(function() { if(scroller.pageelement.clientwidth!=pageelementsize.clientwidth || scroller.pageelement.clientheight!=pageelementsize.clientheight || scroller.pageelement.scrollwidth!=pageelementsize.scrollwidth || scroller.pageelement.scrollheight!=pageelementsize.scrollheight) { pageelementsize.clientwidth = scroller.pageelement.clientwidth; pageelementsize.clientheight = scroller.pageelement.clientheight; pageelementsize.scrollwidth = scroller.pageelement.scrollwidth; pageelementsize.scrollheight = scroller.pageelement.scrollheight; scroller.showbar(); } }, 200); } }; scroller.prototype._resetscrollerelement = function(pageelement) { //重置页面元素 pageelement.style.overflow = 'hidden'; var parentelement = pageelement.parentnode; var values = [['left', domutils.getspacing(parentelement, 'left')], ['right', domutils.getspacing(parentelement, 'right')], ['top', domutils.getspacing(parentelement, 'top')], ['bottom', domutils.getspacing(parentelement, 'bottom')]]; if(parentelement.style.width && parentelement.style.width.indexof('px')!=-1) { pageelement.style.width = parentelement.style.width; pageelement.style.height = parentelement.style.height; } else { pageelement.style.width = ((parentelement.tagname=='body' ? parentelement.ownerdocument.documentelement.clientwidth : parentelement.offsetwidth) - values[0][1] - values[1][1]) + 'px'; pageelement.style.height = ((parentelement.tagname=='body' ? parentelement.ownerdocument.documentelement.clientheight : parentelement.offsetheight) - values[2][1] - values[3][1]) + 'px'; } for(var i=0; i<4; i++) { if(values[i][1]!=0) { eval("parentelement.style.margin" + values[i][0] + " = '0px'; parentelement.style.padding" + values[i][0] + " = '0px'; pageelement.style.margin" + values[i][0] + " = values[i][1] + 'px';"); } } }; try { scroller.prototype.__definesetter__("onscrolling", function(val) { if(!this.onscrollingeventfunctions) { this.onscrollingeventfunctions = []; } this.onscrollingeventfunctions.push(val); }); scroller.prototype.__definesetter__("onafterscroll", function(val) { if(!this.onafterscrolleventfunctions) { this.onafterscrolleventfunctions = []; } this.onafterscrolleventfunctions.push(val); }); scroller.prototype.__definesetter__("onscrollingfromside", function(val) { if(!this.onscrollingfromsideeventfunctions) { this.onscrollingfromsideeventfunctions = []; } this.onscrollingfromsideeventfunctions.push(val); }); scroller.prototype.__definesetter__("onafterscrollfromside", function(val) { if(!this.onafterscrollfromsideeventfunctions) { this.onafterscrollfromsideeventfunctions = []; } this.onafterscrollfromsideeventfunctions.push(val); }); } catch(e) { } scroller.prototype._getparentscroller = function() { //获取父滚动条 for(var obj = this.pageelement; obj; obj = obj.parentnode) { if(obj!=this.pageelement && obj.scroller) { return obj.scroller; } if(obj.tagname=='body' && !(obj=(obj.ownerdocument.parentwindow ? obj.ownerdocument.parentwindow : obj.ownerdocument.defaultview).frameelement)) { break; } } return null; } scroller.prototype._isscrolled = function(type, parentscrolleronly) { //检查是否有滚动过, type: horizontal/vertical/side if(!parentscrolleronly) { var scolled; if(!type) { scolled = this.horizontalscroll || this.verticalscroll || this.sidescroll; } else { eval('scolled = this.' + type + 'scroll;'); } if(scolled) { return true; } } var parentscroller = this._getparentscroller(); return parentscroller && parentscroller._isscrolled(type, false); }; scroller.prototype._ontouchstart = function(event) { if(event.stoppropagation) { event.stoppropagation(); } else { event.cancelbubble = true; } var touch = event.touches[0]; this._resetscroller(); this.begintime = new date().gettime(); this.previousx = touch.screenx; this.previousy = touch.screeny; this.startx = touch.screenx; this.starty = touch.screeny; this.horizontalscroll = false; this.verticalscroll = false; this.sidescroll = false; this.parentscroller = this._getparentscroller(); if(this.inertiascrolltimer && this.inertiascrolltimer>0) { window.clearinterval(this.inertiascrolltimer); this.inertiascrolltimer = 0; } return false; }; scroller.prototype._ontouchmove = function(event) { if(event.stoppropagation) { event.stoppropagation(); } else { event.cancelbubble = true; } var touch = event.touches[0]; var x = this.previousx - touch.screenx; var y = this.previousy - touch.screeny; x = math.abs(x) < scroll_min_step ? 0 : x; y = math.abs(y) < scroll_min_step ? 0 : y; if(x==0 && y==0) { return; } if(this._dosidescroll(this, x, y) || this._doscroll(x, y, scroll_start_step, this.horizontalonly, this.verticalonly)) { if(x!=0) { this.previousx = touch.screenx; } if(y!=0) { this.previousy = touch.screeny; } } }; scroller.prototype._ontouchend = function(event) { if(!this._isscrolled()) { eventutils.clickelement(event.srcelement); return; } if(event.stoppropagation) { event.stoppropagation(); } else { event.cancelbubble = true; } if(this._isscrolled('side')) { //从边框滚动 this._doaftersidescroll(this); return; } var time = new date().gettime() - this.begintime; var inertiatime = 300; if(time>inertiatime || (this.inertiadisabled && (this.horizontalscroll || this.verticalscroll))) { //触摸开始到结束时间大于300毫秒 this._doafterscroll(this); return; } //触摸开始到结束时间小于300毫秒,惯性滚动 var scroller = this; var inertiascroller = this; for(; !inertiascroller.horizontalscroll && !inertiascroller.verticalscroll; inertiascroller=inertiascroller._getparentscroller()); var count = 0; var distance = math.min(3000, (inertiascroller.horizontalscroll ? inertiascroller.pageelement.scrollwidth : inertiascroller.pageelement.scrollheight) / math.max(time/inertiatime*10, 3)); //最多移动3000px var step = math.round(5 + 55 * distance/3000); //步长5~60 var times = distance / step; var xstep = !inertiascroller.horizontalscroll ? 0 : (inertiascroller.startx > this.previousx ? step : -step); var ystep = !inertiascroller.verticalscroll ? 0 : (inertiascroller.starty > this.previousy ? step : -step); this.inertiascrolltimer = window.setinterval(function() { inertiascroller.scroll(xstep, ystep, true, true); if((++count)= top.document.body.clientwidth - scroll_side_width; var istop = this.sidescroll ? this.istop : y!=0 && !isleft && !isright && srcscoller.starty <= scroll_side_width; var isbottom = this.sidescroll ? this.isbottom : y!=0 && !isleft && !isright && srcscoller.starty >= top.document.body.clientheight - scroll_side_width; if(!isleft && !isright && istop && !isbottom) { return false; } //检查当前滚动条以及父滚动条是否有从边框开始滚动事件 for(var i=0; i<(!this.onscrollingfromsideeventfunctions ? 0 : this.onscrollingfromsideeventfunctions.length); i++) { if(this.onscrollingfromsideeventfunctions[i].call(srcscoller, x, y, srcscoller.startx, srcscoller.starty, isleft, isright, istop, isbottom) && !this.sidescroll) { this.sidescroll = true; this.isleft = isleft; this.isright = isright; this.istop = istop; this.isbottom = isbottom; } } if(this.sidescroll) { return true; } var parentscroller = this._getparentscroller(); return parentscroller && parentscroller._dosidescroll(srcscoller, x, y); }; scroller.prototype._doaftersidescroll = function(srcscoller) { //结束从边框滚动 for(var i=0; i<(!this.sidescroll || !this.onafterscrollfromsideeventfunctions ? 0 : this.onafterscrollfromsideeventfunctions.length); i++) { this.onafterscrollfromsideeventfunctions[i].call(this, srcscoller.startx - srcscoller.previousx, srcscoller.starty - srcscoller.previousy, srcscoller.startx, srcscoller.starty, this.isleft, this.isright, this.istop, this.isbottom, true); } var parentscroller = this._getparentscroller(); if(parentscroller) { parentscroller._doaftersidescroll(srcscoller); } }; scroller.prototype._doscroll = function(x, y, startstep, horizontalonly, verticalonly) { //水平或者垂直滚动,递归函数 if(this._isscrolled(null, true)) { //父滚动条已经开始滚动 var parentscroller = this._getparentscroller(); if(parentscroller) { parentscroller._doscroll(x, y, startstep, parentscroller.horizontalscroll, parentscroller.verticalscroll); } return true; } if(!verticalonly && !this.horizontalscroll && (!this.verticalscroll || this.twoway) && math.abs(x)>=startstep) { //不是仅垂直滚动,未开始水平滚动,且未开始垂直滚动或者允许双向滚动,且移动距离大于启动步长 this.horizontalscroll = true; } if(!horizontalonly && !this.verticalscroll && (!this.horizontalscroll || this.twoway) && math.abs(y)>=startstep) { //不是仅水平滚动,未开始垂直滚动,且未开始水平滚动或者允许双向滚动,且移动距离大于启动步长 this.verticalscroll = true; } var scrolled = this.horizontalscroll || this.verticalscroll; if(scrolled) { this.scroll(this.horizontalscroll ? x : 0, this.verticalscroll ? y : 0, false, true); } if((!scrolled && (this.verticalonly || this.horizontalonly)) || (horizontalonly=(this.horizontalscroll && (this.isleft || this.isright))) || (verticalonly=(this.verticalscroll && (this.istop || this.isbottom)))) { var parentscroller = this._getparentscroller(); if(parentscroller) { if(!parentscroller.horizontalscroll && !parentscroller.verticalscroll) { parentscroller.startx = this.previousx; parentscroller.starty = this.previousy; } scrolled = parentscroller._doscroll(x, y, scrolled ? 1 : scroll_start_step, parentscroller.horizontalonly || (scrolled && horizontalonly), parentscroller.verticalonly || (scrolled && verticalonly)) || scrolled; } } return scrolled; }; scroller.prototype._doafterscroll = function(srcscroller) { //结束滚动 for(var i=0; i<((!this.verticalscroll && !this.horizontalscroll) || !this.onafterscrolleventfunctions ? 0 : this.onafterscrolleventfunctions.length); i++) { this.onafterscrolleventfunctions[i].call(null, this.startx - srcscroller.previousx, this.starty - srcscroller.previousy, this.isleft, this.isright, this.istop, this.isbottom, false); } var parentscroller = this._getparentscroller(); if(parentscroller) { parentscroller._doafterscroll(srcscroller); } }; scroller.prototype.scroll = function(x, y, touchend, bytouch) { //touchend:是否结束触摸 if(!this.verticalonly && x!=0) { var left = math.round(this.pageelement.scrollleft + x); this.pageelement.scrollleft = left; this.istop = this.verticalscroll && this.istop; this.isbottom = this.verticalscroll && this.isbottom; this.isleft = this.pageelement.scrollleft > left; this.isright = this.pageelement.scrollleft < left; this.showbar(); } if(!this.horizontalonly && y!=0) { var top = math.round(this.pageelement.scrolltop + y); this.pageelement.scrolltop = top; this.isleft = this.horizontalscroll && this.isleft; this.isright = this.horizontalscroll && this.isright; this.istop = this.pageelement.scrolltop > top; this.isbottom = this.pageelement.scrolltop < top; this.showbar(); } for(var i=0; i<(!this.onscrollingeventfunctions ? 0 : this.onscrollingeventfunctions.length); i++) { this.onscrollingeventfunctions[i].call(null, x, y, this.isleft, this.isright, this.istop, this.isbottom, touchend, !bytouch); } }; scroller.prototype.scrollto = function(top, left) { //滚动到指定位置 this.pageelement.scrollleft = left; this.pageelement.scrolltop = top; this.showbar(); }; scroller.prototype.showbar = function() { if(!this.showscrollbar) { return; } //启动隐藏滚动条定时器 if(!this.alwaysshowscrollbar) { var scroller = this; if(this.hidebartimer) { window.cleartimeout(this.hidebartimer); } this.hidebartimer = window.settimeout(function() { scroller.hidebar(); }, 2000); } if(!this.horizontalonly && (this.isverticalbarvisible || this.alwaysshowscrollbar || this.verticalscroll)) { if(this.pageelement.scrollheight <= this.pageelement.clientheight) { if(this.verticalscroller && this.verticalscroller.parentnode) { this.verticalscroller.parentnode.removechild(this.verticalscroller); } this.verticalscroller = null; this.isverticalbarvisible = false; } else { if(!this.verticalscroller) { //创建垂直滚动条 this._createscrollbar(true); } this.verticalscroller.style.top = this.pageelement.scrolltop + "px"; var height = math.max(30, math.round(this.verticalscroller.offsetheight * this.pageelement.clientheight / this.pageelement.scrollheight)); this.verticalscrollertrack.style.height = height + "px"; this.verticalscrollertrack.style.top = math.round((this.verticalscroller.offsetheight - height) * (this.pageelement.scrolltop / (this.pageelement.scrollheight - this.pageelement.clientheight))) + "px"; this.isverticalbarvisible = true; } } if(!this.verticalonly && (this.ishorizontalbarvisible || this.alwaysshowscrollbar || this.horizontalscroll)) { if(this.pageelement.scrollwidth <= this.pageelement.clientwidth) { if(this.horizontalscroller && this.horizontalscroller.parentnode) { this.horizontalscroller.parentnode.removechild(this.horizontalscroller); } this.horizontalscroller = null; this.ishorizontalbarvisible = false; } else { if(!this.horizontalscroller) { //创建水平滚动条 this._createscrollbar(false); } this.horizontalscroller.style.left = this.pageelement.scrollleft + "px"; var width = math.max(30, this.horizontalscroller.offsetwidth * this.pageelement.clientwidth / this.pageelement.scrollwidth); this.horizontalscrollertrack.style.width = width + "px"; this.horizontalscrollertrack.style.left = math.round((this.horizontalscroller.offsetwidth - width) * (this.pageelement.scrollleft / (this.pageelement.scrollwidth - this.pageelement.clientwidth))) + "px"; this.ishorizontalbarvisible = true; } } }; scroller.prototype._createscrollbar = function(vertical) { var divscroller = this.pageelement.ownerdocument.createelement("div"); divscroller.style.position = "absolute"; divscroller.style.zindex = 1000; var marginleft = cssutils.getelementcomputedstyle(this.pageelement, 'margin-left'); var marginright = cssutils.getelementcomputedstyle(this.pageelement, 'margin-right'); var margintop = cssutils.getelementcomputedstyle(this.pageelement, 'margin-top'); var marginbottom = cssutils.getelementcomputedstyle(this.pageelement, 'margin-bottom'); marginleft = marginleft && marginleft.indexof('px')!=-1 ? number(marginleft.replace('px', '')) : 0; marginright = marginright && marginright.indexof('px')!=-1 ? number(marginright.replace('px', '')) : 0; margintop = margintop && margintop.indexof('px')!=-1 ? number(margintop.replace('px', '')) : 0; marginbottom = marginbottom && marginbottom.indexof('px')!=-1 ? number(marginbottom.replace('px', '')) : 0; divscroller.style.top = (this.pageelement.scolltop + (vertical ? 0 : this.pageelement.clientheight)) + "px"; divscroller.style.left = (this.pageelement.scrollleft + (!vertical ? 0 : this.pageelement.clientwidth)) + "px"; divscroller.style.height = vertical ? (this.pageelement.clientheight + margintop + marginbottom) + "px" : "0px"; divscroller.style.width = vertical ? "0px" : (this.pageelement.clientwidth + marginleft + marginright) + "px"; this.pageelement.appendchild(divscroller); var div = this.pageelement.ownerdocument.createelement("div"); div.classname = 'scrollbar ' + (vertical ? 'vertical' : 'horizontal') + 'scrollbar'; if(vertical) { div.style.height = divscroller.style.height; } else { div.style.width = divscroller.style.width; } divscroller.appendchild(div) var track = this.pageelement.ownerdocument.createelement("div"); track.style.position = "absolute"; track.classname = 'scrollertrack ' + (vertical ? 'vertical' : 'horizontal') + 'scrollertrack'; div.appendchild(track); if(vertical) { this.verticalscroller = divscroller; this.verticalscrollertrack = track; } else { this.horizontalscroller = divscroller; this.horizontalscrollertrack = track; } var scroller = this; track.clientx = -100000; track.clienty = -100000; track.onmousedown = function(event) { //事件处理:鼠标按下 track.ownerdocument.body.onselectstart = function(event) { return false; }; track.clientx = event.clientx; track.clienty = event.clienty; } track.ownerdocument.body.onmousemove = function(event) { //事件处理:鼠标移动 if(track.clientx == -100000) { return; } scroller.scroll(vertical ? 0 : (event.clientx - track.clientx) / scroller.pageelement.clientwidth * scroller.pageelement.scrollwidth, vertical ? (event.clienty - track.clienty) / scroller.pageelement.clientheight * scroller.pageelement.scrollheight : 0, false, false); track.clientx = event.clientx; track.clienty = event.clienty; }; track.ownerdocument.body.onmouseup = function(event) { //事件处理:鼠标松开 track.ownerdocument.body.onselectstart = null; track.clientx = -100000; track.clienty = -100000; }; }; scroller.prototype.hidebar = function() { if(this.alwaysshowscrollbar) { return; } var opacity = 100; var verticalscroller = this.verticalscroller; var horizontalscroller = this.horizontalscroller; this.verticalscroller = null; this.horizontalscroller = null; this.ishorizontalbarvisible = false; this.isverticalbarvisible = false; var hidebartimer = window.setinterval(function() { opacity -= 10; if(opacity<=0) { window.clearinterval(hidebartimer); } if(verticalscroller) { if(opacity<=0) { if(verticalscroller.parentnode) { verticalscroller.parentnode.removechild(verticalscroller); } } else if(document.all) { verticalscroller.style.filter = 'alpha(opacity=' + opacity + ');'; } else { verticalscroller.style.opacity = (opacity / 100.0); } } if(horizontalscroller) { if(opacity<=0) { if(horizontalscroller.parentnode) { horizontalscroller.parentnode.removechild(horizontalscroller); } } else if(document.all) { horizontalscroller.style.filter = 'alpha(opacity=' + opacity + ');'; } else { horizontalscroller.style.opacity = (opacity / 100.0); } } }, 50); }; scroller.prototype.scrollwithtimer = function(targetposition, verticalscroll, minstep, onscrollend) { //定时滚动 var scroller = this; var scrollposition = verticalscroll ? this.pageelement.scrolltop : this.pageelement.scrollleft; var step = math.max((minstep ? minstep : 10), math.ceil(math.abs(scrollposition - targetposition)/5)); var previousposition = 0.1; var scrolltimer = window.setinterval(function() { var position = verticalscroll ? scroller.pageelement.scrolltop : scroller.pageelement.scrollleft; if(position==targetposition || scrollposition!=position || previousposition==position) { //已经移动到位,或者用户手动移动过 window.clearinterval(scrolltimer); if(onscrollend) { onscrollend.call(); } return; } previousposition = position; var size = (position