模拟滚动条是在禁用浏览器自带滚动条后使用html和js代码自定义实现滚动条效果
首先需要设置原有出现滚动条的元素 overfolw:hidden
需要用到的事件有鼠标滚轮事件,mousemove,mouseup,mouseleave,mousedown(其中mousedown事件是放置在滚动条或者滚动滑块上的)
基于上一篇关于队列的文章,我们添加了这些事件
以下代码基于element-ui的表格控件table实现
<div class="el-table__body-scrollbar" ref="scrollBar" :style="[bodyHeight]">
<div class="scroll_block" @mousedown="mouseDown"></div>
</div>
mouseEvent.addEvent(this,this.mouseMove,"mousemove");
mouseEvent.addEvent(this,this.mouseUp,"mouseup");
mouseEvent.addEvent(this,this.mouseLeave,"mouseleave");
mouseEvent.addEvent(this,this.mouseWeel);
html部分就不做全部展示了,将以上代码中的html部分放到合适的地方或者自己写
/**
*this.barHeight滚动条中滚动块的高度
*this.scrollHeight滚动条本身的高度
*this.tableHeight表格内部整体高度所有表格的高度和
*以下所有阻止冒泡事件可以封装
*/
//滚动条中滚动块在鼠标按下时
mouseDown(e){
this.isDrag=true;//标识参数 表示通过可以拖动了
this.pageY=e.pageY;//记录初始的纵向位置
//以下阻止冒泡部分可以封装公用
if(e.stopPropagation) e.stopPropagation();
e.cancelBubble=true;
e.returnValue=false;
return false;
}
mouseMove(event){
if(this.isDrag){
var tar=this.$refs.scrollBar.children.item(0);//没有直接使用ref,取的是滚动块
var mv=event.pageY-this.pageY;//移动量
var top=tar.style.top;//当前滚动块的top 位置
if(top){//如果存在
top=parseFloat(top.replace("px",""));//得出具体数值
if((mv+this.barHeight+top)>this.scrollHeight){//如果移动量加块本身的高度加原有的top位置大于了滚动条的高度
mv=this.scrollHeight-this.barHeight;//移动量保持在最底部
}else if((mv+top)<0){//如果是向上滑动并且超出了顶部 保留在顶部
mv=0;
}else{//正常情况 相加就行
mv=top+mv;
}
}else{
if((mv+this.barHeight)>this.scrollHeight){//同上一种情况,去掉top的影响,去掉相加的情况
mv=this.scrollHeight-this.barHeight;
}else if(mv<0){mv=0;}
}
this.adjust(mv);//具体执行函数,执行滚动条位置变换操作
this.pageY=event.pageY;//保存当前位置
if(!this.simulate){//不可分页 在不可分页的情况 可能实际数据与显示数据不一致 这里是将滚动事件分发到了父组件
this.$emit('bar-scroll',event,mv,this.barHeight);
}
if(event.stopPropagation) event.stopPropagation();
if(event.preventDefault) event.preventDefault();
event.cancelBubble=true;
event.returnValue=false;
return false;
}
}
//这里习惯性的使用了原生js
adjust(mv){
if(typeof mv=='number'){
var tar=this.$refs.scrollBar.children.item(0);
var table=this.bodyWrapper.querySelector(".el-table__body");
tar.style.top=mv+"px";
//到了这里mv代表的就是新的top属性 这是常规算法
//使用marginTop调整块的位置
table.style.marginTop=-(mv*(this.tableHeight-this.scrollHeight))/(this.scrollHeight-this.barHeight)+"px";
}
}
//当鼠标结束按下的状态后
mouseUp(e){
if(this.isDrag){
this.isDrag = false;//标识设置为否
if (e.stopPropagation) e.stopPropagation();
e.cancelBubble = true;
e.returnValue = false;
return false;
}
}
//离开作用范围后
mouseLeave(event){
this.isDrag=false;
},
//鼠标滚轮滚动时的操作
mouseWeel(event){
//先判断作用域范围是否是当前组件
event = event || window.event;
var tar=this.bodyWrapper;
var target=event.target;
if(tar.contains(target)){
var scrollBar=this.$refs.scrollBar;
var bar=scrollBar.children.item(0);
var resize=this.simulate?1:(100/this.total);
var top=bar.style.top,step=(100*this.scrollHeight*resize)/this.tableHeight;
if(scrollBar.style.display!="none"){
top=parseFloat(top.replace("px",""));
if (event.wheelDelta) { //判断浏览器IE,谷歌滑轮事件
//当滑轮向上滚动时
if (event.wheelDelta > 0) {
if((top-step)<=0){
this.adjust(0);
}else{
this.adjust(top-step);
}
if(top>0){
if(event.stopPropagation) event.stopPropagation();
if(event.preventDefault) event.preventDefault();
event.cancelBubble=true;
event.returnValue=false;
return false;
}
}
//当滑轮向下滚动时
if (event.wheelDelta < 0) {
if((top+step+this.barHeight)>=this.scrollHeight){
this.adjust(this.scrollHeight-this.barHeight);
}else{
this.adjust(top+step);
}
if((top+this.barHeight)<(this.scrollHeight-0.01)){
if(event.stopPropagation) event.stopPropagation();
if(event.preventDefault) event.preventDefault();
event.cancelBubble=true;
event.returnValue=false;
return false;
}
}
} else if (event.detail) { //Firefox滑轮事件
if (event.detail< 0) {
if((top-step)<=0){
this.adjust(0);
}else{
this.adjust(top-step);
}
if(top>0){
if(event.stopPropagation) event.stopPropagation();
if(event.preventDefault) event.preventDefault();
event.cancelBubble=true;
event.returnValue=false;
return false;
}
}
if (event.detail> 0) {
if((top+step+this.barHeight)>=this.scrollHeight){
this.adjust(this.scrollHeight-this.barHeight);
}else{
this.adjust(top+step);
}
if((top+this.barHeight)<this.scrollHeight){
if(event.stopPropagation) event.stopPropagation();
if(event.preventDefault) event.preventDefault();
event.cancelBubble=true;
event.returnValue=false;
return false;
}
}
}
}
}
}
}
//执行位置更新操作 并初始化相关参数
//判断是否需要显示滚动条
//代码可能不那么vue 不过不重要
scrollBarSet(){
var scrollBar=this.$refs.scrollBar;
if(scrollBar){
var body=this.bodyWrapper,table=body.querySelector(".el-table__body");
var bar=scrollBar.children.item(0);
var extraHgt=table.offsetWidth>body.offsetWidth?10:0;//设置额外高度。主要是底部滚动条高度这里设置为10
if(body.offsetHeight>=(table.offsetHeight+extraHgt)){
scrollBar.style.display="none";
bar.style.top="";
bar.style.height="";
this.adjust(0);
}else{
scrollBar.style.display="block";
var height=(body.offsetHeight*scrollBar.offsetHeight)/(table.offsetHeight+extraHgt);
if(height<20){//当数据量过大的时候保持滚动块最少20px
height=20;
}
bar.style.height=height+"px";
this.scrollHeight=scrollBar.offsetHeight;
this.tableHeight=table.offsetHeight+extraHgt;
this.barHeight=height;
var top=bar.style.top;
if(top){
top=parseFloat(top.replace("px",""));
if((top+height)>this.scrollHeight){
top=this.scrollHeight-height;
}
}else{top=0;}
this.adjust(top);
}
}
},
以上代码可以实现正常情况下的模拟滚动条功能
如果需要实现在数据量特别大的时候只加载显示部分数据
并且根据相关事件在滚动过程中变换数据实现从显示上加载了全部数据的形式
就需要在实际的控件中手动去切换数据并配合预留的bar-scroll分发实现了
这部分就不贴代码了,完成了这个步骤表格就不会应为数据量而造成严重性能问题
网友评论