因爲遊覽器自帶的滾動條在美觀方面並非很好看,因此不少設計師但願經過本身設計出來的滾動條來作這樣的效果,JS模擬滾動條其實很早看到jQuery有這樣的插件或者KISSY有這樣的組件,一直想着本身何時也來研究下這個神祕的東東,思來想去 作demo 可是也沒有研究出來,一開始有2點不瞭解:一是:當前的滾動條的寬度(水平滾動)或者高度(垂直滾動)不知道怎麼計算? 二是:水平滾動的距離 和 垂直滾動的距離 一次性到底滾動多少?也不知道怎樣計算?因此一直研究不出來!直到最近看到一篇博客關於這方面的 因此才慢慢知道計算方法。css
要JS模擬滾動條須要知道如下知識點:函數
1. 動態設置滾動條的寬度 scrollBarWidth = (容器的寬度 * 容器的寬度 / 內容的寬度)測試
動態設置滾動條的高度 scrollBarHeight = (容器的高度 * 容器的高度 / 內容的高度)this
2. 一次性到底要移動多少距離:計算方法以下:google
xy = (內容的寬度 - 容器的寬度) * 移動的距離 / (容器的寬度 - 滾動條的寬度) 或者 spa
xy = (內容的高度 - 容器的高度) * 移動的距離 / (容器的高度 - 滾動條的高度)
firefox
其中移動的距離 能夠配置的 默認是100prototype
3. 鼠標滾輪事件:判斷是向上滾動(或者向左滾動)仍是向下滾動(或者向右滾動)的遊覽器的兼容性。插件
1. 包括IE6之內的 滾輪事件用 onmousewheel 而firefox一我的還在用DOMMouseScroll事件。而遊覽器判斷是向上滾動仍是向下滾動以下:設計
1. 火狐遊覽器判斷是向下 是經過event.detail這個屬性判斷 若是是-3的話 那麼向下 若是是3的話 那麼向上。
2. 其餘遊覽器是經過event.wheelDelta來判斷 若是是-120的話 那麼向下 不然120的話 是向上
除火狐遊覽器之外 咱們能夠經過如下函數來測試下:
document.body.onmousewheel = function(event) { event = event || window.event; console.log(event.wheelDelta); };
火狐遊覽器能夠經過下面這個函數來測試下:
document.body.addEventListener("DOMMouseScroll", function(event) { console.log(event.detail); });
4. 關於拖動事件: 用到了setCapture 這個東東,這個東東究竟是什麼意思呢?在這以前我也不知道js拖動事件還有一個這樣的東東?通過google才瞭解到:鼠標捕獲(setCapture)做用是將鼠標事件捕獲到當前文檔的指定的對象。這個對象會爲當前應用程序或整個系統接收全部鼠標事件。不過setCapture不支持鍵盤事件, 只能捕獲如下鼠標事件:onmousedown、onmouseup、onmousemove、onclick、ondblclick、onmouseover和onmouseout。
能夠經過這個方法object.releaseCapture() 來釋放.相似於Jquery中的bind 和 unbind綁定事件同樣。
5. 首先咱們能夠根據滾輪事件的兼容性來寫一個方法出來 以下代碼:
/* * 對遊覽器滾輪事件做了兼容處理 * 經過調用函數 判斷 event.delta 是否大於仍是小於0 判斷是向上滾動仍是向下滾動 * win7 火狐遊覽器判斷是向下 是經過event.detail這個屬性判斷 若是是-3的話 那麼向下 或者若是是3的話 那麼向上 * win7 其餘遊覽器是經過event.wheelDelta來判斷 若是是-120的話 那麼向下 不然120的話 是向上 */ _addEvent: function(){ var EventProcess = function(event) { var type = event.type; if(type == 'DOMMouseScroll' || type == 'mousewheel') { event.delta = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3; } if (event.srcElement && !event.target) { event.target = event.srcElement; } if (!event.preventDefault && event.returnValue !== undefined) { event.preventDefault = function() { event.returnValue = false; }; } return event; } if(window.addEventListener) { return function(el,type,fn,capture) { if (type === "mousewheel" && document.mozHidden !== undefined) { type = "DOMMouseScroll"; } el.addEventListener(type, function(event) { fn.call(this, EventProcess(event)); }, capture || false); } }else if (window.attachEvent) { return function(el, type, fn, capture) { el.attachEvent("on" + type, function(event) { event = event || window.event; fn.call(el, EventProcess(event)); }); } } }
而後上面的方法 咱們能夠以下這樣調用 就能夠判斷滾動條是向下(或者向右) 仍是 向上(或者向左)滾動了。
var wheelEvent = self._addEvent(); wheelEvent(container,'mousewheel',function(event){ var wheelDelta = event.delta; if(wheelDelta < 0){ //向下滾動 }else { // 向上滾動 } });
下面來談談我這個JS模擬滾動條組件:
1. 支持可配置水平滾動條和垂直滾動條。
2. 支持頁面上有多個滾動條 只要初始化一次就ok。
3. 默認顯示滾動條 也能夠經過參數isHiddenScroll 爲true 隱藏滾動條 當鼠標移上那塊區域 再顯示滾動條。
4. 默認狀況下須要配置以下幾個參數:
containerCls: 外層容器class類名
contentCls: 內容區域class類名
wrapScrollBarCls: 當前滾動條容器class類名
scrollBarCls : 當前滾動條class類名
sMoveDis: 鼠標單擊擊或滾動滾輪時,滾動條單次移動的距離
isVertical: 是不是垂直滾動條 默認是橫向滾動條 false
isHiddenScroll: 默認狀況下 是否隱藏滾動條 鼠標移上去顯示 默認爲顯示 false
下面咱們來看看效果究竟是個什麼樣的 橫向滾動條以下圖:
衆向滾動條以下:
頁面HTML代碼以下:
<!-- 橫向滾動條 --> <div class="container"> <div class="cont">我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩</div> <div class="wrap-scroll-bar"> <div class="scroll-bar"></div> </div> </div> <div class="container"> <div class="cont" style="width:1000px;">我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩</div> <div class="wrap-scroll-bar"> <div class="scroll-bar"></div> </div> </div> <!-- 衆向滾動條 --> <!-- <div class="container"> <div class="cont">我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩我是龍恩</div> <div class="wrap-scroll-bar"> <div class="scroll-bar"></div> </div> </div>-->
CSS代碼以下:
<style> .container { position:relative; width:500px; height:200px; margin:50px auto 0; overflow: hidden; } .cont { background:#999; color: #fff; height:185px; position:absolute; top:0; left:0; width:2000px; } .wrap-scroll-bar { position:absolute; bottom:0; left:0; height:15px; background:#e6e6e6; width:100%; overflow:hidden; } .scroll-bar { position:absolute; bottom:0; height:15px; background:#ccc; left:0; width:20px; } /** 衆向滾動條css .container { position:relative; width:500px; height:200px; margin:50px auto 0; overflow: hidden; } .cont { color: #fff; background:#999; width:485px; height:1000px; position:absolute; top:0; left:0; } .wrap-scroll-bar { position:absolute; right:0; top:0; width:15px; background:#e6e6e6; height:100%; overflow:hidden; } .scroll-bar { position:absolute; top:0; height:20px; background:#ccc; left:0; width:15px; } **/ .hidden {display:none;}
JS代碼以下:
/** * JS模擬滾動條 * @date 2013-12-06 * @email 879083421 */ function SimulateScroll(options) { this.config = { containerCls : '.container', // 外層容器 contentCls : '.cont', // 內容區域 wrapScrollBarCls : '.wrap-scroll-bar', // 當前滾動條的容器 scrollBarCls : '.scroll-bar', // 當前滾動條 sMoveDis : 100, // 鼠標單擊擊或滾動滾輪時,滾動條單次移動的距離 isVertical : false, // 是不是垂直滾動條 默認是橫向滾動條 isHiddenScroll : false // 默認狀況下是否隱藏滾動條 鼠標移上去顯示 默認爲顯示 }; this.cache = { diX : 0, diY : 0 }; this.init(options); } SimulateScroll.prototype = { init: function(options) { this.config = $.extend(this.config,options || {}); var self = this, _config = self.config, _cache = self.cache; /* * 判斷是不是垂直或者橫向滾動條 * 分別對橫向滾動條或者垂直滾動條初始化寬度或者高度 */ if(!_config.isVertical) { $(_config.containerCls).each(function(index,item) { var containerWidth = $(item).width(), contentWidth = $(_config.contentCls,item).width(); // 設置滾動條按鈕的寬度 (容器的寬度 * 容器的寬度 / 內容的寬度) $(_config.scrollBarCls,item).width(containerWidth * containerWidth /contentWidth); var scrollBarWidth = $(_config.scrollBarCls,item).width(); // 拖動滾動條事件 self._dragScroll(); // 滾動條單擊事件 self._clickScroll(); // 鼠標滾輪事件 self._initMouseWheel(item); }); }else { $(_config.containerCls).each(function(index,item) { var containerHeight = $(item).height(), contentHeight = $(_config.contentCls,item).height(); // 設置滾動條按鈕的高度 (容器的高度 × 容器的高度 / 內容的高度) $(_config.scrollBarCls,item).height(containerHeight * containerHeight /contentHeight); var scrollBarHeight = $(_config.scrollBarCls,item).height(); // 拖動滾動條事件 self._dragScroll(); // 滾動條單擊事件 self._clickScroll(); // 鼠標滾輪事件 self._initMouseWheel(item); }); } // 是否隱藏滾動條 if(_config.isHiddenScroll) { $(_config.wrapScrollBarCls).each(function(index,item){ !$(item).hasClass('hidden') && $(item).addClass('hidden'); }); $(_config.containerCls).each(function(index,item){ $(item).hover(function(){ $(_config.wrapScrollBarCls,item).hasClass("hidden") && $(_config.wrapScrollBarCls,item).removeClass('hidden'); },function(){ !$(_config.wrapScrollBarCls,item).hasClass("hidden") && $(_config.wrapScrollBarCls,item).addClass('hidden'); }) }); } }, /** * 拖動滾動條 */ _dragScroll: function() { var self = this, _config = self.config, _cache = self.cache; /** * 判斷是不是垂直或者橫向滾動條 */ if(!_config.isVertical) { $(_config.scrollBarCls).mousedown(function(e){ _cache.diX = e.pageX - $(this).position().left; if(this.setCapture) { $(this).mousemove(function(event) { var tagParent = $(event.target).closest(_config.containerCls); self._fnChangePos(event.pageX - _cache.diX,tagParent); }); this.setCapture(); //設置捕獲範圍 $(this).mouseup(function() { $(this).unbind('mousemove mouseup'); this.releaseCapture(); //取消捕獲範圍 }); }else { $(document).mousemove(function(event) { var tagParent = $(event.target).closest(_config.containerCls); self._fnChangePos(event.pageX - _cache.diX,tagParent); }); $(document).mouseup(function(){ $(document).unbind('mousemove mouseup'); }); } return false; }); }else { $(_config.scrollBarCls).mousedown(function(e){ _cache.diY = e.pageY - $(this).position().top; if(this.setCapture) { $(this).mousemove(function(event){ var tagParent = $(event.target).closest(_config.containerCls); self._fnChangePos(event.pageY - _cache.diY,tagParent); }); this.setCapture(); //設置捕獲範圍 $(this).mouseup(function() { $(this).unbind('mousemove mouseup'); this.releaseCapture(); //取消捕獲範圍 }); }else { $(document).mousemove(function(event) { var tagParent = $(event.target).closest(_config.containerCls); self._fnChangePos(event.pageY - _cache.diY,tagParent); }); $(document).mouseup(function(){ $(document).unbind('mousemove mouseup'); }); } return false; }); } }, /** * 內容移動距離 * @param xy {string} 移動的距離 * @param tagParent {object} 父節點 * 移動距離的方法 (內容的寬度 - 容器的寬度) * 移動的距離 / (容器的寬度 - 滾動條的寬度) */ _fnChangePos: function(xy,tagParent) { var self = this, _config = self.config; /** * 判斷是不是垂直或者橫向滾動條 */ if(!_config.isVertical) { if(xy < 0) { xy = 0; }else if(xy > $(tagParent).width() - $(_config.scrollBarCls,tagParent).width()) { xy = $(tagParent).width() - $(_config.scrollBarCls,tagParent).width(); } $(_config.scrollBarCls,tagParent).css('left',xy); var left = ($(_config.contentCls,tagParent).width() - $(tagParent).width()) * xy /($(tagParent).width() - $(_config.scrollBarCls,tagParent).width()); $(_config.contentCls,tagParent).css({'left':-left}); }else { if(xy < 0) { xy = 0; }else if(xy > $(tagParent).height() - $(_config.scrollBarCls,tagParent).height()) { xy = $(tagParent).height() - $(_config.scrollBarCls,tagParent).height(); } $(_config.scrollBarCls,tagParent).css('top',xy); var top = ($(_config.contentCls,tagParent).height() - $(tagParent).height()) * xy /($(tagParent).height() - $(_config.scrollBarCls,tagParent).height()); $(_config.contentCls,tagParent).css({'top':-top}); } }, /** * 滾動條單擊事件 */ _clickScroll: function() { var self = this, _config = self.config, _cache = self.cache; $(_config.wrapScrollBarCls).mousedown(function(e){ /** * 判斷是不是垂直或者橫向滾動條 */ if(!_config.isVertical) { var tagParent = $(e.target).closest(_config.containerCls), relDisX = e.pageX - $(this,tagParent).offset().left; /** * relDisX = 鼠標相對於文檔的左邊緣的位置(左邊)- 目標左側相對於文檔的位置 * $(_config.scrollBarCls,tagParent).position().left 指元素相對於父元素的偏移位置 * $(_config.scrollBarCls,tagParent).width() 當前滾動條的寬度 */ if (relDisX > ($(_config.scrollBarCls,tagParent).position().left + $(_config.scrollBarCls,tagParent).width())) { if(_config.sMoveDis <= relDisX) { self._fnChangePos($(_config.scrollBarCls,tagParent).position().left + _config.sMoveDis,tagParent); }else { //console.log('滾動條單次移動的距離過大 請設置小點'); self._fnChangePos($(_config.scrollBarCls,tagParent).position().left + relDisX,tagParent); } } else if (relDisX < $(_config.scrollBarCls,tagParent).position().left) { self._fnChangePos($(_config.scrollBarCls,tagParent).position().left - _config.sMoveDis,tagParent); }; }else { var tagParent = $(e.target).closest(_config.containerCls), relDisY = e.pageY - $(this,tagParent).offset().top; /** * relDisX = 鼠標相對於文檔的左邊緣的位置(左邊)- 目標左側相對於文檔的位置 * $(_config.scrollBarCls,tagParent).position().left 指元素相對於父元素的偏移位置 * $(_config.scrollBarCls,tagParent).width() 當前滾動條的寬度 */ if (relDisY > ($(_config.scrollBarCls,tagParent).position().top + $(_config.scrollBarCls,tagParent).height())) { if(_config.sMoveDis <= relDisY) { self._fnChangePos($(_config.scrollBarCls,tagParent).position().top + _config.sMoveDis,tagParent); }else { //console.log('滾動條單次移動的距離過大 請設置小點'); self._fnChangePos($(_config.scrollBarCls,tagParent).position().top + relDisY,tagParent); } } else if (relDisY < $(_config.scrollBarCls,tagParent).position().top) { self._fnChangePos($(_config.scrollBarCls,tagParent).position().top - _config.sMoveDis,tagParent); }; } }); }, /** * 鼠標滾輪事件 */ _initMouseWheel: function(container) { var self = this, _config = self.config, _cache = self.cache; var wheelEvent = self._addEvent(); wheelEvent(container,'mousewheel',function(event){ var wheelDelta = event.delta; if(wheelDelta < 0){ if(!_config.isVertical) { //滾輪向下滾動 self._fnChangePos($(_config.scrollBarCls,container).position().left + _config.sMoveDis,container); }else { //滾輪向下滾動 self._fnChangePos($(_config.scrollBarCls,container).position().top + _config.sMoveDis,container); } }else { if(!_config.isVertical) { //向上滾動 self._fnChangePos($(_config.scrollBarCls,container).position().left - _config.sMoveDis,container); }else { //向上滾動 self._fnChangePos($(_config.scrollBarCls,container).position().top - _config.sMoveDis,container); } } }); }, /* * 對遊覽器滾輪事件做了兼容處理 * 經過調用函數 判斷 event.delta 是否大於仍是小於0 判斷是向上滾動仍是向下滾動 * win7 火狐遊覽器判斷是向下 是經過event.detail這個屬性判斷 若是是-3的話 那麼向下 或者若是是3的話 那麼向上 * win7 其餘遊覽器是經過event.wheelDelta來判斷 若是是-120的話 那麼向下 不然120的話 是向上 */ _addEvent: function(){ var EventProcess = function(event) { var type = event.type; if(type == 'DOMMouseScroll' || type == 'mousewheel') { event.delta = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3; } if (event.srcElement && !event.target) { event.target = event.srcElement; } if (!event.preventDefault && event.returnValue !== undefined) { event.preventDefault = function() { event.returnValue = false; }; } return event; } if(window.addEventListener) { return function(el,type,fn,capture) { if (type === "mousewheel" && document.mozHidden !== undefined) { type = "DOMMouseScroll"; } el.addEventListener(type, function(event) { fn.call(this, EventProcess(event)); }, capture || false); } }else if (window.attachEvent) { return function(el, type, fn, capture) { el.attachEvent("on" + type, function(event) { event = event || window.event; fn.call(el, EventProcess(event)); }); } } } }; // 代碼初始化 $(function(){ new SimulateScroll({}); });