最近 看了一下网上关于自定义滚动条的描述,有些使用webkit内核的浏览器滚动条实现定制,也有一部分更简单,直接编写成了插件。我个人认为既然是重写网站滚动条,我觉得有必要从底层开始重构一下滚动条,毕竟我们 更喜欢自己亲手打造的东西。下面将进行梳理。
首先游览器都会有自己默认定义的滚动条,我们可以通过通过body,html{height:100%; overflow:hiden;}
的方式对其进行禁止。
下面我们来简要说一下网站滚动条实现的逻辑基础。
滚动条
一般由两部分组成,滚动槽和滚动条。当我们打开一个网站时,滚动条一般不是固定的高度,而是由页面的内容进行 自适应设置(页面越长滚动条相应变短)。参考下图,h为自适应的滚动条高度,H为可视窗口高度,conH为网页内容高度。那么当我们进行自适应设置滚动条时,我们想象一下在拉动滚动条时的效果,滚动条在滚动槽内滚动的比例应该等于可视窗口在页面内容中滚动距离的比例(想象成可视窗口从上到下在页面内容中滑动),那么可以得出h/H=H/conH的适应比例 scale1。由于H,conH可知,此时滚动条的自适应高度便可以动态获得。(记住这个比例关系,下面还要用到)
那么当我们滑动滚动条时,内容的偏移量该如何确定。
此时我们可以推算出一个新的内容比例关系。
内容区与滚动条位移距离的系数
滚动条的偏移量/(视口高度-滚动条高度) = 内容偏移量/(内容高度-视口高度)
==> 滚动条的偏移量 = 内容偏移量* ((视口高度-滚动条高度)/(内容高度-视口高度))
内容偏移量 = 滚动条的偏移量 / ((视口高度-滚动条高度)/(内容高度-视口高度))
内容偏移量 = 滚动条的偏移量 / scale1 (h-H/H-conH=h/H=scale1,推理如下图)
此时只要滚动条滚动的偏移量可以获取,我们便可以设置内容偏移量的数值。那么用鼠标拉动滚动条或者用滚轮控制滚动条移动后,页面内容进行相应滚动的效果 便可以实现。
下面我们只需要关注如何让鼠标拉动滚动条或者用滚轮控制滚动条移动并且获得滚动条滚动的偏移量
鼠标拉动滚动条或者用滚轮控制滚动条移动
1.拖动滚动条事件
我们将滚动条设置成#inner元素块,对滚动条inner元素绑定鼠标按下监听,此时获取鼠标初始位置和元素初始位置,在鼠标按下的事件内绑定鼠标移动监听和松开监听。当我们移动鼠标时获取 鼠标最后位置(通过e参数),鼠标距离差即可得,从而滚动条最新位置=鼠标距离差+滚动条初始位置,在这之前 要做边缘判定,防止滚动条滚出滚动槽。从而内容偏移量可得。最后解绑。
其中有一个 问题,游览器有默认的点击拖动事件,高级游览器可以通过
//禁止浏览器默认行为
return false;
IE8及以下需要给事件绑定全局捕获。inner.setCapture && inner.setCapture();
鼠标拉动滚动条和滚动条最新位置
//滚动条高度
var scaleH = document.documentElement.clientHeight / content.offsetHeight;
inner.style.height = scaleH * document.documentElement.clientHeight + 'px';
//定义元素与鼠标初始位置
var eleY = 0;
var startY = 0;
inner.onmousedown = function(event) {
event = event || window.event;
//元素与鼠标初始位置
eleY = inner.offsetTop;
startY = event.clientY;
//开启全局捕获
inner.setCapture && inner.setCapture();
document.onmousemove = function(event) {
event = event || window.event;
//鼠标结束位置
var endY = event.clientY;
//鼠标距离差
var disY = endY - startY;
//范围限定(对滚动条当前位置进行边缘检测)
var innerTop = disY + eleY;
if(innerTop < 0) {
innerTop = 0;
} else if(innerTop > document.documentElement.clientHeight - inner.offsetHeight) {
innerTop = document.documentElement.clientHeight - inner.offsetHeight
}
//确定元素最终位置
inner.style.top = innerTop + 'px';
//内容区域
//内容偏移量 = 滚动条的偏移量 / scale1
content.style.top = -innerTop / scaleH + 'px';
};
document.onmouseup = function() {
//释放全局捕获
document.releaseCapture && document.releaseCapture();
//解绑
document.onmousemove = document.onmouseup = null;
};
//禁止浏览器默认行为
return false;
};
2.滚轮事件
由于ie/chorme和火狐对滚轮监听不兼容,我们需要做兼容处理。
//ie/chorme滚轮监听的兼容处理
document.onmousewheel = fun;
//火狐滚轮监听的兼容处理 并且避免ie8报错
document.addEventListener && document.addEventListener('DOMMouseScroll', fun);
ie/chorme和火狐的滚轮事件参数也不同,下面进行梳理。
ie/chorme
- 滚轮事件参数:e.wheelDelta
- 滚轮向上返回值:120
- 滚轮向下返回值:-120
火狐
- 滚轮事件参数:e.detail
- 滚轮向上返回值:-3
- 滚轮向下返回值:3
//ie/chorme滚轮监听的兼容处理
document.onmousewheel = fun;
//火狐滚轮监听的兼容处理 并且避免ie8报错
document.addEventListener && document.addEventListener('DOMMouseScroll', fun);
var add = 0;
// add为单次滚轮事件的移动值 +-指代方向
function fun(e) {
e = e || window.event;
//ie/chorme 120为向上 -120向下
if(e.wheelDelta) {
if(e.wheelDelta > 0) {
add = -5;
} else {
add = 5;
}
//火狐 -3向上 3向下
} else if(e.detail) {
if(e.detail < 0) {
add = -5;
} else {
add = 5;
}
}
通过滚轮事件的监听可得滚动轮向上运动还是向下运动,从而计算滚动轮最新位置和内容偏移量。
最终效果图和实现代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
overflow: hidden;
}
#scroll {
width: 20px;
height: 100%;
background-color: gray;
position: absolute;
right: 0;
top: 0;
}
#scroll #inner {
width: 20px;
/*height: 60px;*/
position: absolute;
left: 0;
top: 0;
background-color: whitesmoke;
}
#content {
position: absolute;
left: 0;
top: 0;
}
</style>
</head>
<body>
<!--内容区域-->
<div id="content"></div>
<!--滑槽-->
<div id="scroll">
<!--滑块-->
<div id="inner"></div>
</div>
</body>
<script type="text/javascript">
window.onload = function() {
var content = document.getElementById('content');
var inner = document.getElementById('inner');
//造文本
for(var i = 0; i < 300; i++) {
content.innerHTML += (i + 1) + '<br>'
};
//滚动条高度
var scaleH = document.documentElement.clientHeight / content.offsetHeight;
inner.style.height = scaleH * document.documentElement.clientHeight + 'px';
//定义元素与鼠标初始位置
var eleY = 0;
var startY = 0;
inner.onmousedown = function(event) {
event = event || window.event;
//元素与鼠标初始位置
eleY = inner.offsetTop;
startY = event.clientY;
//开启全局捕获
inner.setCapture && inner.setCapture();
document.onmousemove = function(event) {
event = event || window.event;
//鼠标结束位置
var endY = event.clientY;
//鼠标距离差
var disY = endY - startY;
//范围限定(对滚动条当前位置进行边缘检测)
var innerTop = disY + eleY;
if(innerTop < 0) {
innerTop = 0;
} else if(innerTop > document.documentElement.clientHeight - inner.offsetHeight) {
innerTop = document.documentElement.clientHeight - inner.offsetHeight
}
//确定元素最终位置
inner.style.top = innerTop + 'px';
//内容区域
//内容偏移量 = 滚动条的偏移量 / scale1
content.style.top = -innerTop / scaleH + 'px';
};
document.onmouseup = function() {
//释放全局捕获
document.releaseCapture && document.releaseCapture();
//解绑
document.onmousemove = document.onmouseup = null;
};
//禁止浏览器默认行为
return false;
};
//滚轮事件
//ie/chorme滚轮监听的兼容处理
document.onmousewheel = fun;
//火狐滚轮监听的兼容处理 并且避免ie8报错
document.addEventListener && document.addEventListener('DOMMouseScroll', fun);
var add = 0;
// add为单次滚轮事件的移动值 +-指代方向
function fun(e) {
e = e || window.event;
//ie/chorme 120为向上 -120向下
if(e.wheelDelta) {
if(e.wheelDelta > 0) {
add = -5;
} else {
add = 5;
}
//火狐 -3向上 3向下
} else if(e.detail) {
if(e.detail < 0) {
add = -5;
} else {
add = 5;
}
}
//滚动条新位置
var innerOffsetTop = inner.offsetTop + add;
//边缘判断
if(innerOffsetTop < 0) {
innerOffsetTop = 0;
} else if(innerOffsetTop > document.documentElement.clientHeight - inner.offsetHeight) {
innerOffsetTop = document.documentElement.clientHeight - inner.offsetHeight;
}
//滚动条和内容新位置的设置
inner.style.top = innerOffsetTop + 'px';
content.style.top = -inner.offsetTop / scaleH + 'px';
//取消游览器默认行为
event.preventDefault && event.preventDefault();
return false;
}
}
</script>
</html>
更多推荐
网站滚动条的重构(附详细代码和注释)
发布评论