原生js實現(xiàn)模擬滾動條
當頁面中有很多滾動條,它們相互嵌套,很不好看,這時就會模擬滾動條,并給這個滾動條好看的樣式,使得頁面美觀。
模擬滾動條很多時候是去用jquery插件,然后寫幾行代碼就搞定了。不過隨著mvvm的快速發(fā)展,很多時候都懶得用jquery了,這就是本文的動機,力求用簡單的不依賴jquery只依賴mvvm(avalon) api的代碼,完成一個簡易的滾動條。
要求:
1.鼠標滾輪可以讓滾動條工作,界面滾動
2.鼠標可以拖動滾動條并讓界面滾動
3.頁面resize時,滾動條根據(jù)頁面尺寸變化,仍然可以工作
效果:

很顯然,這個組件是基于拖動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中,添加一個鉤子,允許從外面添加一個監(jiān)聽函數(shù),拖動時會觸發(fā)監(jiān)聽函數(shù),并傳入drag參數(shù)。
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組件拖動時的監(jiān)聽函數(shù),更新容器視圖
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(){//更新組件所有部分視圖,并暴露供外部調(diào)用
//容器高度這里設(shè)置成瀏覽器可視部分-容器垂直方向位置,沒有考慮容器有border,padding,margin.可根據(jù)相應場景修改
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時,調(diào)用了drag組件的refresh方法,更新滾動條可以拖動的范圍。這里暴露了refresh_views()方法,以應對外部需要手動更新視圖的情況。比如,聊天分組的折疊和展開。

這樣就完成了簡易滾動條。代碼很簡單,如果出問題需要fix bug或定制的話,也很容易。
以上所述上就是本文的全部內(nèi)容了,希望大家能夠喜歡。
相關(guān)文章
深入了解Hybrid App技術(shù)的相關(guān)知識
這篇文章主要介紹了深入了解Hybrid App技術(shù)的相關(guān)知識,Hybrid App(混合模式移動應用)是指介于web-app、native-app這兩者之間的app,兼具" Native App良好用戶交互體驗的優(yōu)勢 "和" Web App跨平臺開發(fā)的優(yōu)勢 ",需要的朋友可以參考下2019-07-07
js setTimeout()函數(shù)介紹及應用以倒計時為例
setTimeout() 方法用于在指定的毫秒數(shù)后調(diào)用函數(shù)或計算表達式,下面有個倒計時的示例,需要的朋友可以學習下2013-12-12
JavaScript股票的動態(tài)買賣規(guī)劃實例分析下篇
這篇文章主要介紹了JavaScript對于動態(tài)規(guī)劃解決股票問題的真題例舉講解。文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-08-08

