javascript控件开发之滚动条控件

在网页中,<DIV>本身就有滚动条,在显示文本内容的时候,原始的滚动条已够用,一般如果我们想实现一个类似列表的控件时,也可以把所有的列表数据输出到一个完整的<table>标签,再嵌入到一个DIV中即可,然而如果数据量达到几千行时,完全生成<table>的话,将会非常的占用内存,因此较好的办法是用虚拟展示的方式,
所谓虚拟展示就是把数据用一个数组存储, 然后只生成可见部分的DOM元素,比如可见行是10行,那就创建10个<tr>,通过刷新的方式,在这10行中,滚动显示所有的数据,这样,滚动条就是主要的一环节,
以上的说明等到我们做后面的列表展示控件时,大家就会明白,在此就不再详细说明,接下来我们进入主题,开发我们的滚动条控件。
本次我们做的滚动特性有,上、下两个按钮和拖动的按钮,其余特性遵照平时系统滚动条的规则来开发,其中滚动区域是通过设置明细数量来设置滚动数量,比如,总共有1000条数据,然后界面能显示50条,那滚动数量则是1000-50条,通过这个数据来绘制滚动条的滚动数量,这种方式实现滚动条是为后续虚拟滚动做准备
首先,基于行前几篇开发的的框架,我们在目录 component\ui\下添加文件 com.ui.scrollBar.js, 在文件中定义com.ui.scrollBar类,继承com.ui.window类,如下


/**
* 滚动条控件.
* 创建: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;
},

定义好基本的属性后,开始绘制滚动条的框架, 这里我们采用<TABLE>来实现, 比如纵向滚动条,则生成一个三行一列的<TABLE>,第一行与第三行用作上下按钮,中间用作滚动区域,(横向的同理),
然后在滚动区域(上边<table>的第二行)中又加入一个两行一列的<table>,用作中间拖动按钮,第一行,绘制边线与背景相同,第二行绘制成按钮,通过调整第一行的行高来设定按钮的位置,
绘制完成后,添加按钮的事件,及拖动事件, 如下:


/**
* 渲染.
*/
render:function() {
this.base();
//创建滚动条结构<table>
if(this.scrollTable == null) {
this.scrollTable = this.createElement("TABLE");
}
//清除原有的行列,结构变化的时候,会有已存在行
while(this.scrollTable.rows.length > 0) {
this.scrollTable.deleteRow(0);
}
//创建拖动按钮结构<TABLE>
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函数就是用于控制滚动条的样式及位置的,

/**
* 刷新布局
*/
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";
},

这上边用到几个样式,在com.comStyle.css文件中定义如下:


.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函数,如下:

/**
*跟据滚动数据,刷新滚动条位置
*/
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,doMouseUp事件来实现,如下:

/**
*鼠标按下事件
*/
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;
},

下面为鼠标移动事件,当拖动状态dragState等于true时,跟据鼠标的起始位置及当前位置计算滚动按钮的移动距离,并计算相应的滚动明细位置scrollIndex,并且执行滚动事件onScroll绑定的事件,由于滚动事件属于外部事件,这里作异常捕获处理。

/**
*鼠标移动事件
*/
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.html文件中添加两个滚动条控件的标签,如下id=test12/13的两个控件:

<!DOCTYPE html>
<head><title>test</title>
<script src="../script/common/init.js" type="text/javascript"></script>
</head>
<body code="controllor/test.js" scroll="no" style="overflow:hidden">
<div id='test6' code='com.ui.panel' option='{"align":"alTop","height":"60","width":"200","borderWidth":1,"padding":1}'>
<div id='test8' code='com.ui.toolButton' option='{"height":"60","width":"60","icon":"image/test3.ico"}'>确定</div>
<div id='test9' code='com.ui.toolButton' option='{"height":"60","width":"60","icon":"image/test2.ico"}'>取消</div>
<div id='test10' code='com.ui.toolButton' option='{"height":"60","width":"60","type":"split"}'></div>
<div id='test11' code='com.ui.toolButton' option='{"height":"60","width":"60","icon":"image/test1.ico"}'>退出</div>
</div>
<div id='test1' code='com.ui.panel' option='{"align":"alTop","height":"40","width":"200","borderWidth":1,"padding":1}'></div>
<div id='test4' code='com.ui.panel' option='{"align":"alBottom","height":"50","width":"200","borderWidth":1,"padding":1}'>
<div id='test2' code='com.ui.button' option='{"height":"25","width":"100","top":"12","right":"116"}'></div>
<div id='test3' code='com.ui.button' option='{"height":"25","width":"100","top":"12","right":"8"}'></div>
</div>
<div id='test7' code='com.ui.panel' option='{"align":"alLeft","height":"100","width":"200","borderWidth":1,"padding":1}'></div>
<div id='test5' code='com.ui.panel' option='{"align":"alClient","height":"100","width":"200","borderWidth":1,"padding":1}'>
<div id='test12' code='com.ui.scrollBar' option='{"height":"200","width":"20","scrollType":"V","borderWidth":1}'></div>
<div id='test13' code='com.ui.scrollBar' option='{"height":"20","width":"200","scrollType":"H","borderWidth":1}'></div>
</div>
</body>
</html>


然后我们在页面控制器中添加一些代码,

/**
* 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();
}


如此就可以打开test.html页面查看效果,如图

[img]http://dl2.iteye.com/upload/attachment/0101/6742/3eb11f6a-ea05-3581-92c7-c7999076ae66.png[/img]

写到此又靠一段落了,因为前期比较忙,更新的时间长了些,这一编有点长但是思路比较简单,实现难度也不算大,希望大家能看懂,
如果想看效果,可以下载源码包查看
请关注我的下一编,javascript控件开发之列表控件(1)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值