首页 > 编程 > JavaScript > 正文

原生js实现模拟滚动条

2019-11-20 12:15:12
字体:
来源:转载
供稿:网友

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

模拟滚动条很多时候是去用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里的方法及冗余代码

define('drag',['avalon-min'],function(avalon){  function getBoundary(container, target) {    var borderTopWidth = 0, borderRightWidth = 0, borderBottomWidth = 0, borderLeftWidth = 0, cOffset = avalon(container)    .offset(), cOffsetTop = cOffset.top, cOffsetLeft = cOffset.left, tOffset = avalon(target)    .offset();    borderTopWidth = parseFloat(avalon.css(container,'borderTopWidth'));    borderRightWidth = parseFloat(avalon.css(container,'borderRightWidth'));    borderBottomWidth = parseFloat(avalon.css(container,'borderBottomWidth'));    borderLeftWidth = parseFloat(avalon.css(container,'borderLeftWidth'));    cOffsetTop = cOffsetTop - tOffset.top + parseFloat(avalon(target).css('top'));    cOffsetLeft = cOffsetLeft - tOffset.left + parseFloat(avalon(target).css('left'));    return {      top : cOffsetTop + borderTopWidth,      right : cOffsetLeft + avalon(container).outerWidth() - avalon(target).outerWidth()      - borderRightWidth,      left : cOffsetLeft + borderLeftWidth,      bottom : cOffsetTop + avalon(container).outerHeight() - avalon(target).outerHeight()      - borderBottomWidth    };  }  var drag = function(target, options) {    var defaults = {      axis:null,      container:null,      handle:null,      ondragmove:null    };    var o =avalon.mix(defaults,options),    doc = target.ownerDocument,    win = doc.defaultView || doc.parentWindow,    originHandle=target,    isIE =!-[1,],    handle = isIE ? target :doc,    container = o.container ?o.container: null,     count = 0,    drag = this,        axis = o.axis,        isMove = false,     boundary, zIndex, originalX, originalY,    clearSelect = 'getSelection' in win ? function(){      win.getSelection().removeAllRanges();    } : function(){      try{        doc.selection.empty();      }      catch( e ){};    },    down = function( e ){      o.isDown = true;              var newTarget = target,      left, top, offset;      o.width = avalon(target).outerWidth();      o.height = avalon(target).outerHeight();      o.handle = handle;      left = avalon(newTarget).css( 'left' );      top = avalon(newTarget).css( 'top' );           offset = avalon(newTarget).offset();      drag.left = left = parseInt( left );      drag.top = top = parseInt( top );      drag.offsetLeft = offset.left;      drag.offsetTop = offset.top;      originalX = e.pageX - left;      originalY = e.pageY - top;       if( (!boundary && container)){        boundary = getBoundary(container, newTarget );       }       if( axis ){        if( axis === 'x' ){          originalY = false;        }        else if( axis === 'y' ){          originalX = false;        }      }      if( isIE ){        handle.setCapture();      }      avalon.bind(handle,'mousemove',move);      avalon.bind(handle,'mouseup',up);      if( isIE ){        avalon.bind(handle,'losecapture',up);      }      e.stopPropagation();      e.preventDefault();      },    move = function( e ){      if( !o.isDown ){        return;      }            count++;      if( count % 2 === 0 ){        return;      }      var currentX = e.pageX,      currentY = e.pageY,      style = target.style,      x, y, left, right, top, bottom;      clearSelect();      isMove = true;      if( originalX ){        x = currentX - originalX;        if( boundary ){          left = boundary.left;          right = boundary.right;          x = x < left ? left :           x > right ? right :          x;        }          drag.left = x;        drag.offsetLeft = currentX - e.offsetX;        style.left = x + 'px';      }      if( originalY ){        y = currentY - originalY;        if( boundary ){          top = boundary.top;          bottom = boundary.bottom;          y = y < top ? top :           y > bottom ? bottom :          y;        }          drag.top = y;        drag.offsetTop = currentY - e.offsetY;        style.top = y + 'px';      }      o.ondragmove.call(this,drag);      e.stopPropagation();      },    up = function( e ){      o.isDown = false;      if( isIE ){        avalon.unbind(handle,'losecapture' );      }      avalon.unbind( handle,'mousemove');      avalon.unbind( handle,'mouseup');      if( isIE ){        handle.releaseCapture();      }      e.stopPropagation();            };     avalon(originHandle).css( 'cursor', 'pointer' );    avalon.bind( originHandle,'mousedown', down );    drag.refresh=function(){      boundary=getBoundary(container,target);    };    };  return drag;});

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

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

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

o.ondragmove.call(this,drag);

然后是scrollbar.js

define('scrollbar',['avalon-min','drag'],function(avalon,drag){  function scrollbar(wrap,scrollbar,height_per_scroll){//容器,滚动条,每次滚轮移动的距离    this.scroll_height=0;//滚动条高度    this.dragger=null;//drag组件实例    wrap.scrollTop=0;    //容器的位置要减去浏览器最外面的默认滚动条垂直方向位置    var self=this,wrap_top=avalon(wrap).offset().top-avalon(document).scrollTop();    function ondragmove(drag){//drag组件拖动时的监听函数,更新容器视图      wrap.scrollTop=(parseFloat(scrollbar.style.top)-wrap_top)*      (wrap.scrollHeight -wrap.clientHeight)/(wrap.clientHeight-self.scroll_height);    };    function setScrollPosition(o) {//更新滚动条位置      scrollbar.style.top =o.scrollTop*wrap.clientHeight/wrap.scrollHeight+wrap_top+ 'px';    }    function inti_events(){      avalon.bind(wrap,'mousewheel',function(e){        if(e.wheelDelta < 0)          wrap.scrollTop+=height_per_scroll;        else          wrap.scrollTop-=height_per_scroll;        setScrollPosition(wrap);        e.preventDefault();       });      self.dragger=new drag(scrollbar,{container:wrap,axis:'y',ondragmove:ondragmove});      window.onresize=function(){        self.refresh_views();        self.dragger.refresh();      };    }    this.refresh_views=function(){//更新组件所有部分视图,并暴露供外部调用      //容器高度这里设置成浏览器可视部分-容器垂直方向位置,没有考虑容器有border,padding,margin.可根据相应场景修改      wrap.style.height=document.documentElement.clientHeight-wrap_top+'px';      self.scroll_height=wrap.clientHeight*wrap.clientHeight/wrap.scrollHeight;      //容器高度等于滚动条高度,隐藏滚动条      if(self.scroll_height==wrap.clientHeight)        scrollbar.style.display='none';      else        scrollbar.style.display='block';      scrollbar.style.height=self.scroll_height+'px';      setScrollPosition(wrap);    }    function init(){      self.refresh_views();      inti_events();    }    init();  }  return scrollbar;});

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

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

以上所述上就是本文的全部内容了,希望大家能够喜欢。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表