你的位置:首页 > Java教程

[Java教程]js模拟滚动条(不依赖jquery)


转载请注明: TheViper http://www.cnblogs.com/TheViper

当页面中有很多滚动条,它们相互嵌套,很不好看,这时就会模拟滚动条,并给这个滚动条好看的样式,使得页面美观。

模拟滚动条很多时候是去用jquery插件,然后写几行代码就搞定了。不过随着mvvm的快速发展,很多时候都懒得用jquery了,这就是本文的动机,本屌力求用简单的不依赖jquery只依赖mvvm(avalon) api的代码,完成一个简易的滚动条。

要求:

1.鼠标滚轮可以让滚动条工作,界面滚动

2.鼠标可以拖动滚动条并让界面滚动

3.页面resize时,滚动条根据页面尺寸变化,仍然可以工作

效果:

很显然,这个组件是基于拖动drag的,本屌又不想重新写,就只有改下ui框架的drag了,这里改的是easy js ui的drag组件。用easy js是因为注释比较多,代码简洁。

本屌把easy js ui的drag组件里的相应方法换成avalon api里的方法,删掉prototype里的方法及冗余代码

 1 define('drag',['avalon-min'],function(avalon){ 2   function getBoundary(container, target) { 3     var borderTopWidth = 0, borderRightWidth = 0, borderBottomWidth = 0, borderLeftWidth = 0, cOffset = avalon(container) 4     .offset(), cOffsetTop = cOffset.top, cOffsetLeft = cOffset.left, tOffset = avalon(target) 5     .offset(); 6     borderTopWidth = parseFloat(avalon.css(container,'borderTopWidth')); 7     borderRightWidth = parseFloat(avalon.css(container,'borderRightWidth')); 8     borderBottomWidth = parseFloat(avalon.css(container,'borderBottomWidth')); 9     borderLeftWidth = parseFloat(avalon.css(container,'borderLeftWidth')); 10     cOffsetTop = cOffsetTop - tOffset.top + parseFloat(avalon(target).css('top')); 11     cOffsetLeft = cOffsetLeft - tOffset.left + parseFloat(avalon(target).css('left')); 12     return { 13       top : cOffsetTop + borderTopWidth, 14       right : cOffsetLeft + avalon(container).outerWidth() - avalon(target).outerWidth() 15       - borderRightWidth, 16       left : cOffsetLeft + borderLeftWidth, 17       bottom : cOffsetTop + avalon(container).outerHeight() - avalon(target).outerHeight() 18       - borderBottomWidth 19     }; 20   } 21   var drag = function(target, options) { 22     var defaults = { 23       axis:null, 24       container:null, 25       handle:null, 26       ondragmove:null 27     }; 28     var o =avalon.mix(defaults,options), 29     doc = target.ownerDocument, 30     win = doc.defaultView || doc.parentWindow, 31     originHandle=target, 32     isIE =!-[1,], 33     handle = isIE ? target :doc, 34     container = o.container ?o.container: null,  35     count = 0, 36     drag = this,     37     axis = o.axis,     38     isMove = false,  39     boundary, zIndex, originalX, originalY, 40     clearSelect = 'getSelection' in win ? function(){ 41       win.getSelection().removeAllRanges(); 42     } : function(){ 43       try{ 44         doc.selection.empty(); 45       } 46       catch( e ){}; 47     }, 48     down = function( e ){ 49       o.isDown = true;         50       var newTarget = target, 51       left, top, offset; 52       o.width = avalon(target).outerWidth(); 53       o.height = avalon(target).outerHeight(); 54       o.handle = handle; 55       left = avalon(newTarget).css( 'left' ); 56       top = avalon(newTarget).css( 'top' );      57       offset = avalon(newTarget).offset(); 58       drag.left = left = parseInt( left ); 59       drag.top = top = parseInt( top ); 60       drag.offsetLeft = offset.left; 61       drag.offsetTop = offset.top; 62       originalX = e.pageX - left; 63       originalY = e.pageY - top;  64       if( (!boundary && container)){ 65         boundary = getBoundary(container, newTarget );  66       }  67       if( axis ){ 68         if( axis === 'x' ){ 69           originalY = false; 70         } 71         else if( axis === 'y' ){ 72           originalX = false; 73         } 74       } 75       if( isIE ){ 76         handle.setCapture(); 77       } 78       avalon.bind(handle,'mousemove',move); 79       avalon.bind(handle,'mouseup',up); 80       if( isIE ){ 81         avalon.bind(handle,'losecapture',up); 82       } 83       e.stopPropagation(); 84       e.preventDefault();   85     }, 86     move = function( e ){ 87       if( !o.isDown ){ 88         return; 89       }       90       count++; 91       if( count % 2 === 0 ){ 92         return; 93       } 94       var currentX = e.pageX, 95       currentY = e.pageY, 96       style = target.style, 97       x, y, left, right, top, bottom; 98       clearSelect(); 99       isMove = true;100       if( originalX ){101         x = currentX - originalX;102         if( boundary ){103           left = boundary.left;104           right = boundary.right;105           x = x < left ? left : 106           x > right ? right :107           x;108         }  109         drag.left = x;110         drag.offsetLeft = currentX - e.offsetX;111         style.left = x + 'px';112       }113       if( originalY ){114         y = currentY - originalY;115         if( boundary ){116           top = boundary.top;117           bottom = boundary.bottom;118           y = y < top ? top : 119           y > bottom ? bottom :120           y;121         }  122         drag.top = y;123         drag.offsetTop = currentY - e.offsetY;124         style.top = y + 'px';125       }126       o.ondragmove.call(this,drag);127       e.stopPropagation();  128     },129     up = function( e ){130       o.isDown = false;131       if( isIE ){132         avalon.unbind(handle,'losecapture' );133       }134       avalon.unbind( handle,'mousemove');135       avalon.unbind( handle,'mouseup');136       if( isIE ){137         handle.releaseCapture();138       }139       e.stopPropagation();        140     }; 141     avalon(originHandle).css( 'cursor', 'pointer' );142     avalon.bind( originHandle,'mousedown', down );143     drag.refresh=function(){144       boundary=getBoundary(container,target);145     };  146   };147   return drag;148 });

View Code

另外在最后暴露的drag上加了一个refresh()方法,作用是在resize时,需要更新滚动条可以拖动的范围。这个方法在scrollbar的更新视图中会用到。

    drag.refresh=function(){      boundary=getBoundary(container,target);    }; 

还有在滚动条拖动过程move中,添加一个钩子,允许从外面添加一个监听函数,拖动时会触发监听函数,并传入drag参数。

o.ondragmove.call(this,drag);

然后是scrollbar.js

 1 define('scrollbar',['avalon-min','drag'],function(avalon,drag){ 2   function scrollbar(wrap,scrollbar,height_per_scroll){//容器,滚动条,每次滚轮移动的距离 3     this.scroll_height=0;//滚动条高度 4     this.dragger=null;//drag组件实例 5     wrap.scrollTop=0; 6     //容器的位置要减去浏览器最外面的默认滚动条垂直方向位置 7     var self=this,wrap_top=avalon(wrap).offset().top-avalon(document).scrollTop(); 8     function ondragmove(drag){//drag组件拖动时的监听函数,更新容器视图 9       wrap.scrollTop=(parseFloat(scrollbar.style.top)-wrap_top)*10       (wrap.scrollHeight -wrap.clientHeight)/(wrap.clientHeight-self.scroll_height);11     };12     function setScrollPosition(o) {//更新滚动条位置13       scrollbar.style.top =o.scrollTop*wrap.clientHeight/wrap.scrollHeight+wrap_top+ 'px';14     }15     function inti_events(){16       avalon.bind(wrap,'mousewheel',function(e){17         if(e.wheelDelta < 0)18          wrap.scrollTop+=height_per_scroll;19         else20          wrap.scrollTop-=height_per_scroll;21         setScrollPosition(wrap);22         e.preventDefault(); 23       });24       self.dragger=new drag(scrollbar,{container:wrap,axis:'y',ondragmove:ondragmove});25       window.onresize=function(){26         self.refresh_views();27         self.dragger.refresh();28       };29     }30     this.refresh_views=function(){//更新组件所有部分视图,并暴露供外部调用31       //容器高度这里设置成浏览器可视部分-容器垂直方向位置,没有考虑容器有border,padding,margin.可根据相应场景修改32       wrap.style.height=document.documentElement.clientHeight-wrap_top+'px';33       self.scroll_height=wrap.clientHeight*wrap.clientHeight/wrap.scrollHeight;34       //容器高度等于滚动条高度,隐藏滚动条35       if(self.scroll_height==wrap.clientHeight)36         scrollbar.style.display='none';37       else38         scrollbar.style.display='block';39       scrollbar.style.height=self.scroll_height+'px';40       setScrollPosition(wrap);41     }42     function init(){43       self.refresh_views();44       inti_events();45     }46     init();47   }48   return scrollbar;49 });

可以看到,在resize时,调用了drag组件的refresh方法,更新滚动条可以拖动的范围。这里暴露了refresh_views()方法,以应对外部需要手动更新视图的情况。比如,聊天分组的折叠和展开。

这样就完成了简易滚动条。代码很简单,如果出问题需要fix bug或定制的话,也很容易。

最后附上下载

如果您觉得本文的内容对您有所帮助,您可以打赏我: