• 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         //position()获取目标节点的所有父节点中,离目标节点最近且position属性是relative,absolute或fixed的父亲节点与目标节点的位置差,
     7         //由于是相对的,所以不用考虑滚动条
     8         var self=this,wrap_top=avalon(wrap).position().top;
     9         function ondragmove(drag){//drag组件拖动时的监听函数,更新容器视图
    10             wrap.scrollTop=(parseFloat(scrollbar.style.top)-wrap_top)*
    11             (wrap.scrollHeight -wrap.clientHeight)/(wrap.clientHeight-self.scroll_height);
    12         };
    13         function setScrollPosition(o) {//更新滚动条位置
    14             scrollbar.style.top =o.scrollTop*wrap.clientHeight/wrap.scrollHeight+wrap_top+ 'px';
    15         }
    16         function inti_events(){
    17             avalon.bind(wrap,'mousewheel',function(e){
    18                 if(e.wheelDelta < 0)
    19                    wrap.scrollTop+=height_per_scroll;
    20                 else
    21                    wrap.scrollTop-=height_per_scroll;
    22                 setScrollPosition(wrap);
    23                 e.preventDefault(); 
    24             });
    25             self.dragger=new drag(scrollbar,{container:wrap,axis:'y',ondragmove:ondragmove});
    26             window.onresize=function(){
    27                 self.refresh_views();
    28                 self.dragger.refresh();
    29             };
    30         }
    31         this.refresh_views=function(){//更新组件所有部分视图,并暴露供外部调用
    32             //容器高度这里设置成浏览器可视部分-容器垂直方向位置,没有考虑容器有border,padding,margin.可根据相应场景修改
    33             wrap.style.height=document.documentElement.clientHeight-wrap_top+'px';
    34             self.scroll_height=wrap.clientHeight*wrap.clientHeight/wrap.scrollHeight;
    35             //容器高度等于滚动条高度,隐藏滚动条
    36             if(self.scroll_height==wrap.clientHeight)
    37                 scrollbar.style.display='none';
    38             else
    39                 scrollbar.style.display='block';
    40             scrollbar.style.height=self.scroll_height+'px';
    41             setScrollPosition(wrap);
    42         }
    43         function init(){
    44             self.refresh_views();
    45             inti_events();
    46         }
    47         init();
    48     }
    49     return scrollbar;
    50 });

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

    注意这里没有考虑容器存在margin,padding,border的情况,如果需要请自行修改。

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

    最后附上下载

  • 相关阅读:
    常用公式 距离、波形、力
    代码字体
    关于flash缩放的详细解释
    色调
    工程项目1
    使用double无法得到数学上的精确结果的原因及为何不能用double来初始化BigDecimal
    第一次测验感受
    原码,补码,反码的概念及Java中使用那种存储方式
    static的含义
    第一次测试代码
  • 原文地址:https://www.cnblogs.com/TheViper/p/4561740.html
Copyright © 2020-2023  润新知