javascript控制項開發之捲軸控制項
在網頁中,本身就有捲軸,在顯示常值內容的時候,原始的捲軸已夠用,一般如果我們想實現一個類似列表的控制項時,也可以把所有的列表資料輸出到一個完整的
標籤,再嵌入到一個DIV中即可,然而如果資料量達到幾千行時,完全產生
/** * 捲軸控制項. * 建立:QZZ * 日期:2014-03-01 */(function(undefined) {nameSpace("com.ui");/** * 捲軸控制項. */com.ui.scrollBar = Extend(com.ui.window, { /** * 建立函數 */ create:function() { }, /** * 渲染. */ render:function() { }});})();
/** * 建立函數 */create:function() { this.base(); this.className = "com.ui.scrollBar"; this.logInfo("create");//綁定明細總數量this.scrollCount = 0;//顯示地區可見數量this.showCount = 0;//滾動位置this.scrollIndex = 0;//每步滾動像素長this.stepLen = 2;//拖動按鈕最短長this.dragBoxMinLen = 20;//橫、縱向捲軸this.option.scrollType = this.option.scrollType || "V";//捲軸是否可見this.visible = true;//按鈕寬度this.btnWidth = 15;//初始化寬高屬性if(this.option.scrollType == "V") { this.option.width = this.option.width || 16; this.option.height = this.option.height || 100;} else if(this.option.scrollType == "H") { this.option.width = this.option.width || 100; this.option.height = this.option.height || 16;}this.dragState = false;},
的話,將會非常的佔用記憶體,因此較好的辦法是用虛擬展示的方式, 所謂虛擬展示就是把資料用一個數組儲存, 然後只產生可見部分的DOM元素,比如可見行是10行,那就建立10個
,通過重新整理的方式,在這10行中,滾動顯示所有的資料,這樣,捲軸就是主要的一環節, 以上的說明等到我們做後面的列表展示控制項時,大家就會明白,在此就不再詳細說明,接下來我們進入主題,開發我們的捲軸控制項。 本次我們做的滾動特性有,上、下兩個按鈕和拖動的按鈕,其餘特性遵照平時系統捲軸的規則來開發,其中捲動區域是通過設定明細數量來設定滾動數量,比如,總共有1000條資料,然後介面能顯示50條,那滾動數量則是1000-50條,通過這個資料來繪製捲軸的滾動數量,這種方式實現捲軸是為後續虛擬滾動做準備 首先,基於行前幾篇開發的的架構,我們在目錄 component\ui\下添加檔案 com.ui.scrollBar.js, 在檔案中定義com.ui.scrollBar類,繼承com.ui.window類,如下在捲軸的建立函數中定義一些基本屬性,如捲軸類型、滾動總數量、顯示數量、滾動位置、滾動一條的步長及拖動按鈕最短長度等,如下 定義好基本的屬性後,開始繪製捲軸的架構, 這裡我們採用
,第一行與第三行用作上下按鈕,中間用作捲動區域,(橫向的同理), 然後在捲動區域(上邊
/** * 渲染. */ render:function() { this.base(); //建立捲軸結構
,用作中間拖動按鈕,第一行,繪製邊線與背景相同,第二行繪製成按鈕,通過調整第一行的行高來設定按鈕的位置, 繪製完成後,添加按鈕的事件,及拖動事件, 如下:
if(this.scrollTable == null) { this.scrollTable = this.createElement("TABLE"); } //清除原有的行列,結構變化的時候,會有已存在行 while(this.scrollTable.rows.length > 0) { this.scrollTable.deleteRow(0); } //建立拖動按鈕結構
/** * 重新整理布局 */ refreshBox:function() { var rw = this._getRectWidth(), rh = this._getRectHeight() //設定按鈕的樣式及長、寬度, if(this.option.scrollType == "V") { this.priorBtn.style.height = this.btnWidth - 1 + "px"; this.setStyle(this.priorBtn, "scrollUp"); this.centerRect.style.height = (rh - 2 * this.btnWidth) + "px"; this.setStyle(this.centerRect, "scrollCell"); this.nextBtn.style.height = this.btnWidth - 1 + "px"; this.setStyle(this.nextBtn, "scrollDown"); this.dragTable.style.width = (rw - 1) + "px"; this.dragLen.style.height = "0px"; this.dragBox.style.height = this.btnWidth + "px"; } else if(this.option.scrollType == "H") { this.priorBtn.style.width = this.btnWidth + "px"; this.setStyle(this.priorBtn, "scrollPrior"); this.centerRect.style.width = (rw - 2 * this.btnWidth) + "px"; this.setStyle(this.centerRect, "scrollCell"); this.nextBtn.style.width = this.btnWidth + "px"; this.setStyle(this.nextBtn, "scrollNext"); this.dragTable.style.height = rh + "px"; this.dragLen.style.width = "0px"; this.dragBox.style.width = this.btnWidth + "px"; } //設定捲動區域的樣式,及拖動按鈕的樣式, this.setStyle(this.dragLen, "scrollCell"); this.setStyle(this.dragBox, "scrollDrag"); //設定滾動表格和拖動表格的樣式, this.setStyle(this.scrollTable, "scrollBox"); this.setStyle(this.dragTable, "scrollBox"); //設定寬、高 this.scrollTable.style.width = rw + "px"; this.scrollTable.style.height = rh + "px"; },
.scrollBox { border-collapse:collapse; border-spacing:0px;padding:0px;}.scrollCell { padding:0px;vertical-align:top;background-color:#eeeeee;}.scrollUp { padding:0px;background-color:#ddeeff;border-Bottom:1px solid #C3D2E6;}.scrollDown { padding:0px;background-color:#ddeeff;border-Top:1px solid #C3D2E6;}.scrollPrior { padding:0px;background-color:#ddeeff;border-right:1px solid #C3D2E6;}.scrollNext { padding:0px;background-color:#ddeeff;border-Left:1px solid #C3D2E6;}.scrollDrag { padding:0px;background-color:#ddeeff;border:1px solid #C3D2E6;}
/** *跟據滾動資料,重新整理捲軸位置 */ refresh:function() { //計算捲動區域外的數量 var sl = this.scrollCount - this.showCount; //發生了變化才重新計算 if(sl != this.tmpCount) { //計算總象素長度 var slpx = sl * this.stepLen; //計算拖動框長度 var cenLen = this.option.scrollType == "V"? this.centerRect.offsetHeight:this.centerRect.offsetWidth; var dragBoxLen = cenLen - slpx; //如果計算出來的拖動按鈕過短,則重新計算步長,保證按鈕不再縮短 if(dragBoxLen < this.dragBoxMinLen) { dragBoxLen = this.dragBoxMinLen; slpx = cenLen - dragBoxLen; this.stepLen = slpx/(this.scrollCount - this.showCount); } else { this.stepLen = 2; } //設定拖動按鈕長度 if(this.option.scrollType == "V") { this.dragBox.style.height = (dragBoxLen - 2) + "px"; } else if(this.option.scrollType == "H") { this.dragBox.style.width = (dragBoxLen - 2) + "px"; } //緩衝當前數量 this.tmpCount = sl; } //計算拖動框位置 var len = parseInt(this.scrollIndex * this.stepLen, 10); if(len < 0) { len = 0; } else if(len > cenLen - dragBoxLen) { len = cenLen - dragBoxLen } //設定位置值 if(this.option.scrollType == "V") { this.dragLen.style.height = len + "px"; } else if(this.option.scrollType == "H") { this.dragLen.style.width = len + "px"; } },
/** *滑鼠按下事件 */ doMouseDown:function(ev) { this.base(ev); //記錄原滑鼠的按下位置 this.dragX = ev.clientX; this.dragY = ev.clientY; //記錄原按鈕的位置, if(this.option.scrollType == "V") { this.dragPosition = this.dragLen.offsetHeight; } else if(this.option.scrollType == "H") { this.dragPosition = this.dragLen.offsetWidth; } //標識按鈕狀態 this.mouseDown = true; }, /** *滑鼠彈起事件 */ doMouseUp:function(ev) { this.base(ev); //還原拖動狀態 this.dragState = false; //取消長按效果 if(this.setInt != null) { clearInterval(this.setInt); this.setInt = null; } //標識按鈕狀態 this.mouseDown = false; },
/** *滑鼠移動事件 */ doMouseMove:function(ev) { if(this.dragState) { var len = 0, dragBoxLen = 0, centerLen = 0; //計算設定拖動按鈕的位置, if(this.option.scrollType == "V") { dragBoxLen = this._domValue(this.dragBox.style.height); centerLen = this._domValue(this.centerRect.style.height); len = this.dragPosition + ev.clientY - this.dragY; if(len < 0) { len = 0; } else if(len > centerLen - dragBoxLen) { len = centerLen - dragBoxLen; } this.dragLen.style.height = len + "px"; } else if(this.option.scrollType == "H") { dragBoxLen = this._domValue(this.dragBox.style.width); centerLen = this._domValue(this.centerRect.style.width); len = this.dragPosition + ev.clientX - this.dragX; if(len < 0) { len = 0; } else if(len > centerLen - dragBoxLen) { len = centerLen - dragBoxLen - 2; } this.dragLen.style.width = len + "px"; } //計算明細數量位置 var tmpScrollIndex = this.scrollIndex; if(dragBoxLen < 1) { this.scrollIndex = 0; } else if(len >= centerLen - dragBoxLen) { this.scrollIndex = this.scrollCount - this.showCount; } else { this.scrollIndex = parseInt(len/this.stepLen, 10); } //執行滾動事件 if(typeof this.onScroll == "function") { try { this.onScroll(this.scrollIndex, this.scrollIndex - tmpScrollIndex); } catch(e) { this.logInfo("onScroll Error!" + e.message); } } } },
/** * 向上滾動 */ scrollPrior:function() { if(this.scrollIndex > 0) { this.scrollIndex --; if(typeof this.onScroll == "function") { this.onScroll(this.scrollIndex, -1); } this.refresh(); } }, /** * 向下滾動 */ scrollNext:function() { if(this.scrollIndex < this.scrollCount - this.showCount) { this.scrollIndex ++; if(typeof this.onScroll == "function") { try { this.onScroll(this.scrollIndex, 1); } catch(e) { this.logInfo("onScroll Error!" + e.message); } } this.refresh(); } }, /** * 向上滾動翻頁 */ scrollPriorPage:function() { var tmpIndex = this.scrollIndex; this.scrollIndex += this.showCount; if(this.scrollIndex > this.scrollCount - this.showCount) { this.scrollIndex = this.scrollCount - this.showCount; } if(tmpIndex != this.scrollIndex) { if(typeof this.onScroll == "function") { try { this.onScroll(this.scrollIndex, this.scrollIndex - tmpIndex); } catch(e) { this.logInfo("onScroll Error!" + e.message); } } this.refresh(); } }, /** * 向下滾動翻頁 */ scrollNextPage:function() { var tmpIndex = this.scrollIndex; this.scrollIndex -= this.showCount; if(this.scrollIndex < 0) { this.scrollIndex = 0; } if(tmpIndex != this.scrollIndex) { if(typeof this.onScroll == "function") { try { this.onScroll(this.scrollIndex, this.scrollIndex - tmpIndex); } catch(e) { this.logInfo("onScroll Error!" + e.message); } } this.refresh(); } },
/** *捲軸總數量 */ setScrollCount:function(value) { this.scrollCount = value; }, /** *捲軸顯示數量 */ setShowCount:function(value) { if(value >= this.scrollCount) { this.showCount = this.scrollCount; this.dragTable.style.display = "none"; } else { this.showCount = value; this.dragTable.style.display = ""; } }, /** *捲軸滾到第幾個位置 */ setScrollIndex:function(index) { if(index < 0) { index = 0; } else if(index > this.scrollCount - this.showCount) { index = this.scrollCount - this.showCount; } if(this.scrollIndex != index) { var tmpIndex = this.scrollIndex; this.scrollIndex = index; if(typeof this.onScroll == "function") { try { this.onScroll(this.scrollIndex, this.scrollIndex - tmpIndex); } catch(e) { this.logInfo("onScroll Error!" + e.message); } } } },
/** * 設定高度. */setHeight:function(height) { this.base(height);this.refreshBox();this.refresh();},/** * 設定寬度. */setWidth:function(width) { this.base(width);this.refreshBox();this.refresh();}
test <script src="../script/common/init.js" type="text/javascript"></script> 確定 取消 退出
/** * test.html頁面控制類 * 建立:qzz * 日期: 2014-05-01 */(function(undefined) { /** * 控制類 */ testControllor = Extend(pageControllor, { //初始化頁面 initPage:function() { //修改頁面上的控制項屬性,綁定事件 this.test2.onClick = function() { alert("確定"); } this.test2.setCaption("確定"); this.test2.setWidth(50); this.test3.onClick = function() { alert("取消"); } this.test3.setCaption("取消"); this.test11.onClick = function() { alert("退出"); } this.test12.setScrollCount(50); this.test12.setShowCount(10); this.test12.setScrollIndex(17); this.test12.refresh(); this.test13.setScrollCount(100); this.test13.setShowCount(10); this.test13.setScrollIndex(17); this.test13.setWidth(400); //this.test13.refresh(); } });})();/** * 頁面對象建立,且啟動 */thisWindow.onShow = function() { var tc = new testControllor(); tc.initPage();}
if(this.dragTable == null) { this.dragTable = this.createElement("TABLE"); } //清除原有行列,結構變化的時候,會有已存在行, while(this.dragTable.rows.length > 0) { this.dragTable.deleteRow(0); } //插入行列 if(this.option.scrollType == "V" && this.scrollTable.rows.length != 3) { this.priorBtn = this.scrollTable.insertRow(0).insertCell(0); this.centerRect = this.scrollTable.insertRow(1).insertCell(0); this.nextBtn = this.scrollTable.insertRow(2).insertCell(0); this.dragLen = this.dragTable.insertRow(0).insertCell(0); this.dragBox = this.dragTable.insertRow(1).insertCell(0); this.centerRect.appendChild(this.dragTable); } else if(this.option.scrollType == "H" && this.scrollTable.rows.length != 1) { var tr = this.scrollTable.insertRow(0); this.priorBtn = tr.insertCell(0); this.centerRect = tr.insertCell(1); this.nextBtn = tr.insertCell(2); var tr1 = this.dragTable.insertRow(0); this.dragLen = tr1.insertCell(0); this.dragBox = tr1.insertCell(1); this.centerRect.appendChild(this.dragTable); } var _this = this; //拖動框滑鼠按下事件, 並列印標誌dragState,把控制項置為焦點 this.dragBox.onmousedown = function(ev) { //拖動狀態 _this.dragState = true; _this.focus = true; } //上一個按鈕滑鼠按下事件,這裡處理長按時的效果,採用順延強制的方式, //scrollPrior()函數,是後續定義的向上滾動一行的函數, this.setInt = null; this.priorBtn.onmousedown = function(ev) { _this.scrollPrior(); _this.mouseDown = true; setTimeout(function() { if(_this.mouseDown) { _this.setInt = setInterval(function() { _this.scrollPrior(); if(_this.scrollIndex == _this.scrollCount - _this.showCount) { clearInterval(_this.setInt); } }, 50);//setInterval } }, 500); //setTimeout } //下一個按鈕滑鼠按下事件,這裡處理長按時的效果,採用順延強制的方式, //scrollNext()函數,是後續定義的向下滾動一行的函數, this.nextBtn.onmousedown = function(ev) { _this.scrollNext(); _this.mouseDown = true; setTimeout(function() { if(_this.mouseDown) { _this.setInt = setInterval(function() { _this.scrollNext(); if(_this.scrollIndex == _this.scrollCount - _this.showCount) { clearInterval(_this.setInt); }; }, 50); //setInterval } }, 500); //setTimeout } //中間地區滑鼠按下事件, 控制向下、上翻頁, //scrollPriorPage, scrollNextPage是後繼定義的翻頁函數。 this.centerRect.onmousedown = function(ev) { var ev = ev || event; if(_this.dragState != true) { var dlen = 0, clen; //debugger; var rect = this.getBoundingClientRect(); if(_this.option.scrollType == "V") { dlen = _this.dragLen.offsetHeight; clen = ev.clientY - rect.top; } else if(_this.option.scrollType == "H") { dlen = _this.dragLen.offsetWidth; clen = ev.clientX - rect.left; } if(clen > dlen) { _this.scrollPriorPage(); } else { _this.scrollNextPage(); } } } //添加到win容器中 this.win.appendChild(this.scrollTable); //重新整理捲軸框, 該方法為後繼定義的函數,用於重新整理滾動的樣式及位置。 this.refreshBox(); },上邊我們只是繪製出捲軸的結構,需要進行樣式控制,下邊refreshBox函數就是用於控制捲軸的樣式及位置的,這上邊用到幾個樣式,在com.comStyle.css檔案中定義如下:完成以上結構後,只是完成了顯示的效果,因為我們這個捲軸是通過設定資料明細數量來設定滾動的,因此需要與象素進行轉換計算,以設定拖動按鈕的位置,下面我們添加一個重新整理函數,用於重新整理捲軸的長度、拖動按鈕的長度及位置,添加refresh函數,如下:下面我們實現 拖動的事件,首先要先在滑鼠按鍵的時候初始化一些屬性,再在滑鼠彈起的時候,還原一些屬性,這裡我能通過重寫doMouseDown,doMouseUp事件來實現,如下:下面為滑鼠移動事件,當拖動狀態dragState等於true時,跟據滑鼠的起始位置及當前位置計算滾動按鈕的移動距離,並計算相應的滾動明細位置scrollIndex,並且執行滾動事件onScroll綁定的事件,由於滾動事件屬於外來事件,這裡作異常捕獲處理。下面我們再定義上邊用到的上、下明細函數,及上、下翻頁函數,如下:接著,再定義幾個屬性的設定函數,如下:最後設定高度,寬度的函數,到此我們的全部控制項編寫完成,從上邊來看,有點長,但是這點長絕對值得,因為後續控制項,將會大大降低實現難度,這裡有個缺點,就是沒有給向上、下按鈕添加圖片,不過,如果需要只需要修改樣式檔案,把圖片加上即可,當然,我們寫了控制,那麼就要測試一下,下面我們寫幾行代碼,用來測試控制項的使用,首先在test.html檔案中添加兩個捲軸控制項的標籤,如下id=test12/13的兩個控制項:然後我們在頁面控制器中添加一些代碼,如此就可以開啟test.html頁面查看效果,下載源碼包查看請關注我的下一編,javascript控制項開發之清單控制項(1)