JavaScript 实现自定义滚动条:从基础到流畅交互的优化

作者:微光时间:2026-10-28 00:27:50浏览:252

本文通过两个JavaScript示例对比,演示如何自制滚动条。初始版本存在拖动卡顿问题,通过引入setCapture和releaseCapture机制优化鼠标事件捕获,显著提升了滚动条拖动的流畅度与用户体验。

JavaScript 实现自定义滚动条:从基础到流畅交互的优化

本文展示了一个使用JavaScript编写的自定义滚动条示例,首先可以通过点击链接查看演示效果。

这里提供了两个演示版本,右侧黑色样式的滚动条是我最初编写的较为简单的代码实现。

具体代码实现如下:

var scrollself=(function(){
var scrollblock, //滚动块
 scrollcontent, //被滚动的内容
 scrollbar, //滚动条
 scrollpanel, //滚动内容的滚动区域
 cdistance, //滚动内容要滚动的距离
 bdistance, //滚动块要滚动的距离
 minuTop, //滚动条头尾剩下的空白
 cTop, //滚动内容的top
 startY=0, //滚动动作开始初鼠标的位置
 bTop=0, //滚动动作开始初滚动块的top
 isDrag=false; //是否拉动滚动块
function prevent(e){
if(e.preventDefault){
 e.preventDefault();
 }
 if(e.stopPropagation){
 e.stopPropagation();
 }
 e.cancelBubble=true;
 e.returnValue=false;
 }
function mouseDown(event){
 isDrag=true;
 event=event||window.event;
 startY=event.clientY;
 bTop=scrollblock.offsetTop;
 cTop=scrollcontent.offsetTop;
// prevent(event);
    }
function mouseMove(event){
 if(isDrag){
event=event||window.event;
 var newbTop=event.clientY-startY+bTop,
 newcTop=cTop-(event.clientY-startY)/bdistance*cdistance;
if(newbTop<minuTop){
 newbTop=minuTop;
 newcTop=0;
 }else{
 if(newbTop>bdistance+minuTop){
 newcTop=-cdistance;
 newbTop=bdistance+minuTop;
 }
 }
scrollblock.style.top=newbTop+'px';
 scrollcontent.style.top=newcTop+'px';
 }else{
 isDrag=false;
 }
// prevent(event);
 }
function mouseUp(event){
isDrag=false;
// prevent(event);
 }
function addHandler(){
 scrollblock.onmousedown=mouseDown;
 scrollblock.onmousemove=mouseMove;
 scrollblock.onmouseup=mouseUp;
 document.onmouseup=mouseUp;
 }
return{
 init:function(scrollpanel_id,scrollcontent_id,scrollbar_id,scrollblock_id){
 scrollblock=document.getElementById(scrollblock_id);
 scrollcontent=document.getElementById(scrollcontent_id);
 scrollbar=document.getElementById(scrollbar_id);
 scrollpanel=document.getElementById(scrollpanel_id);
 minuTop=scrollblock.offsetTop;
 cdistance=scrollcontent.offsetHeight-scrollpanel.offsetHeight;
 bdistance=scrollbar.offsetHeight-minuTop*2-scrollblock.offsetHeight;
 enclose(scrollpanel,scrollcontent,scrollbar,scrollblock,bdistance,cdistance,minuTop);
 addHandler();
 }
 }
}());
scrollself.init('scrollpanel2','scrollcontent2','scrollbar2','scrollblock2');

基础版本的问题与优化思路

之所以称其为基础版本,是因为对比两个演示效果可以发现,右侧黑色滚动条在拖动时体验较差,存在明显的卡顿感,而左侧绿色版本的流畅度则高很多。

由于初始版本存在卡顿问题,我参考了其他代码思路并进行了修改,从而得到了左侧绿色流畅版本的Demo,效果明显改善,具体代码如下:

具体代码实现如下:

var scroll=(function(){
var scrollblock, //滚动块
 scrollcontent, //被滚动的内容
 scrollbar, //滚动条
 scrollpanel, //滚动内容的滚动区域
 cdistance, //滚动内容要滚动的距离
 bdistance, //滚动块要滚动的距离
 minuTop, //滚动条头尾剩下的空白
 cTop, //滚动内容的top
 startY=0, //滚动动作开始初鼠标的位置
 bTop=0; //滚动动作开始初滚动块的top
function mouseDown(event){
 event=event||window.event;
 startY=event.clientY;
 bTop=scrollblock.offsetTop;
 cTop=scrollcontent.offsetTop;
 if(scrollblock.setCapture){
 scrollblock.onmousemove=doDrag;
 scrollblock.onmouseup=stopDrag;
 scrollblock.setCapture();
 }else{
 document.addEventListener("mousemove",doDrag,true);
 document.addEventListener("mouseup",stopDrag,true);
 }
 }
function doDrag(event){
 event=event||window.event;
 var newbTop=event.clientY-startY+bTop,
 newcTop=cTop-(event.clientY-startY)/bdistance*cdistance;
if(newbTop<minuTop){
 newbTop=minuTop;
 newcTop=0;
 }else if(newbTop>bdistance+minuTop){
 newcTop=-cdistance;
 newbTop=bdistance+minuTop;
 }
 scrollblock.style.top=newbTop+'px';
 scrollcontent.style.top=newcTop+'px';
    }
function stopDrag(event){
 if(scrollblock.releaseCapture){
scrollblock.onmousemove=doDrag;
 scrollblock.onmouseup=stopDrag;
 scrollblock.releaseCapture();
 }else{
 document.removeEventListener("mousemove",doDrag,true);
 document.removeEventListener("mouseup",stopDrag,true);
 }
 scrollblock.onmousemove=null;
 scrollblock.onmouseup=null;
 }
 return{
 init:function(scrollpanel_id,scrollcontent_id,scrollbar_id,scrollblock_id){
 scrollblock=document.getElementById(scrollblock_id);
 scrollcontent=document.getElementById(scrollcontent_id);
 scrollbar=document.getElementById(scrollbar_id);
 scrollpanel=document.getElementById(scrollpanel_id);
 minuTop=scrollblock.offsetTop;
 cdistance=scrollcontent.offsetHeight-scrollpanel.offsetHeight;
 bdistance=scrollbar.offsetHeight-minuTop*2-scrollblock.offsetHeight;
 scrollblock.onmousedown=mouseDown;
 enclose(scrollpanel,scrollcontent,scrollbar,scrollblock,bdistance,cdistance,minuTop);
 }
 }
}());
scroll.init('scrollpanel','scrollcontent','scrollbar','scrollblock');

关键优化:setCapture与releaseCapture机制

对比两段代码,主要差异在于流畅版本(左侧绿色)中引入了setCapture和releaseCapture机制。

关于这些机制的具体工作原理,后续可以进一步深入研究。

作者最新文章