下面我来详细讲解如何原生js实现自定义滚动条组件。
1. 确定组件需求
在实现自定义滚动条组件前,我们需要先确定组件的需求。一般来说,自定义滚动条组件需要具备以下功能:
- 拖动滑块来实现滚动;
- 点击轨道实现精准跳转;
- 自适应内容高度,并根据显示内容的变化而动态更新滚动条长度;
- 能够跨浏览器平台使用。
2. 组件结构设计
组件的结构设计需要包含以下元素:
- 一条轨道,包含一个可拖动的滑块;
- 显示内容区域。
因此,我们可以参考以下代码:
<div class="scroll-wrapper">
<div class="scroll-content">
// 显示内容区域
</div>
<div class="scroll-track">
<div class="scroll-bar"></div>
// 滑块
</div>
</div>
3. 编写组件的CSS样式
接下来,我们需要编写组件的CSS样式。需要注意的是,我们需要判断浏览器是否需要添加滚动条。如果需要,则隐藏浏览器默认滚动条,并添加自定义滚动条组件。
以下是CSS样式的示例代码:
/* 隐藏默认滚动条 */
body::-webkit-scrollbar {
display: none;
}
/* 组件样式 */
.scroll-wrapper {
position: relative;
overflow: hidden;
/* 容器尺寸 */
height: 300px;
width: 400px;
}
.scroll-content {
position: absolute;
/* 内容尺寸 */
height: 100%;
width: 100%;
/* 添加padding,避免出现遮挡 */
padding-right: 15px;
}
.scroll-track {
position: absolute;
/* 轨道尺寸 */
right: 0;
top: 0;
bottom: 0;
width: 10px;
/* 背景色 */
background: #f1f1f1;
}
.scroll-bar {
position: absolute;
/* 滑块尺寸 */
top: 0;
left: 0;
width: 100%;
height: 50px;
/* 滑块样式 */
background-color: #333;
border-radius: 5px;
cursor: pointer;
}
4. 实现滚动条组件功能
4.1 功能1:拖动滑块实现滚动
实现步骤:
- 获取
scroll-bar
元素; - 为
scroll-bar
元素添加鼠标按下、鼠标移动、鼠标松开事件; - 鼠标按下时,记录滑块相对于轨道顶部的距离,记录滑块上一次的位置;
- 鼠标移动时,计算滑块当前位置,并将其移动到该位置上;
- 鼠标松开时,将事件绑定移除。
以下是实现代码:
let scrollBar = document.querySelector('.scroll-bar');
scrollBar.addEventListener('mousedown', (e) => {
// 记录相对于轨道顶部的距离
let offsetY = e.clientY - scrollBar.offsetTop;
// 记录滑块上一次的位置
let lastY = scrollBar.offsetTop;
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
function onMouseMove(e) {
// 计算滑块当前位置
let y = e.clientY - offsetY;
// 滑块不能超出轨道
if (y < 0) {
y = 0;
}
let trackHeight = document.querySelector('.scroll-track').offsetHeight;
if (y > trackHeight - scrollBar.offsetHeight) {
y = trackHeight - scrollBar.offsetHeight;
}
// 将滑块移动到当前位置
scrollBar.style.top = y + 'px';
// 根据滑块位置计算出内容区应该滚动的距离
let contentHeight = document.querySelector('.scroll-content').offsetHeight;
let scrollDistance = y / (trackHeight - scrollBar.offsetHeight) * (contentHeight - trackHeight);
// 将内容区移动到当前位置
document.querySelector('.scroll-content').scrollTop = scrollDistance;
}
function onMouseUp() {
// 移除事件绑定
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
}
});
4.2 功能2:点击轨道跳转
实现步骤:
- 获取
scroll-track
元素; - 为
scroll-track
元素添加点击事件; - 计算出滑块的新位置,并将滑块移动到该位置上。
以下是实现代码:
let scrollTrack = document.querySelector('.scroll-track');
scrollTrack.addEventListener('click', (e) => {
// 计算出滑块的新位置
let trackHeight = scrollTrack.offsetHeight;
let barHeight = scrollBar.offsetHeight;
let y = e.clientY - scrollTrack.getBoundingClientRect().top - barHeight / 2;
if (y < 0) {
y = 0;
} else if (y > trackHeight - barHeight) {
y = trackHeight - barHeight;
}
// 将滑块移动到新位置
scrollBar.style.top = y + 'px';
// 根据滑块位置计算出内容区应该滚动的距离
let contentHeight = document.querySelector('.scroll-content').offsetHeight;
let scrollDistance = y / (trackHeight - barHeight) * (contentHeight - trackHeight);
// 将内容区移动到当前位置
document.querySelector('.scroll-content').scrollTop = scrollDistance;
});
4.3 功能3:自适应内容高度并更新滚动条长度
实现步骤:
- 获取
scroll-wrapper
元素和scroll-content
元素; - 在
scroll-content
元素高度变化时,计算并更新滑块的长度。
以下是实现代码:
let scrollWrapper = document.querySelector('.scroll-wrapper');
let scrollContent = document.querySelector('.scroll-content');
let mutationObserver = new MutationObserver((mutations) => {
// 计算滑块长度
let trackHeight = scrollWrapper.offsetHeight;
let barHeight = Math.pow(trackHeight, 2) / scrollContent.scrollHeight;
if (barHeight < 50) {
barHeight = 50;
}
// 更新滑块长度
scrollBar.style.height = barHeight + 'px';
});
mutationObserver.observe(scrollContent, {
childList: true,
subtree: true,
characterData: true,
attributes: true
});
5. 示例说明
以下是两个使用示例:
示例1:自定义滚动条组件使用示例
<div class="scroll-wrapper">
<div class="scroll-content">
<p>苟利国家生死以,岂因祸福避趋之!</p>
<p>千磨万击还坚劲,任尔东西南北风。</p>
<p>雨打梨花深闭门,忘了青春,误了青春。</p>
<p>靡不有初,鲜克有终。</p>
<p>未曾谋面时,心中缪隐似若神仙。</p>
<p>前事不忘,后事之师。</p>
<p>性格决定命运。</p>
<p>不在高山,不在深谷。</p>
<p>天空没有翅膀的痕迹,但鸟儿已经飞过。</p>
</div>
<div class="scroll-track">
<div class="scroll-bar"></div>
</div>
</div>
<style>
body::-webkit-scrollbar {
display: none;
}
.scroll-wrapper {
position: relative;
overflow: hidden;
/* 容器尺寸 */
height: 300px;
width: 400px;
}
.scroll-content {
position: absolute;
/* 内容尺寸 */
height: 100%;
width: 100%;
/* 添加padding,避免出现遮挡 */
padding-right: 15px;
}
.scroll-track {
position: absolute;
/* 轨道尺寸 */
right: 0;
top: 0;
bottom: 0;
width: 10px;
/* 背景色 */
background: #f1f1f1;
}
.scroll-bar {
position: absolute;
/* 滑块尺寸 */
top: 0;
left: 0;
width: 100%;
height: 50px;
/* 滑块样式 */
background-color: #333;
border-radius: 5px;
cursor: pointer;
}
</style>
<script>
let scrollBar = document.querySelector('.scroll-bar');
scrollBar.addEventListener('mousedown', (e) => {
// 记录相对于轨道顶部的距离
let offsetY = e.clientY - scrollBar.offsetTop;
// 记录滑块上一次的位置
let lastY = scrollBar.offsetTop;
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
function onMouseMove(e) {
// 计算滑块当前位置
let y = e.clientY - offsetY;
// 滑块不能超出轨道
if (y < 0) {
y = 0;
}
let trackHeight = document.querySelector('.scroll-track').offsetHeight;
if (y > trackHeight - scrollBar.offsetHeight) {
y = trackHeight - scrollBar.offsetHeight;
}
// 将滑块移动到当前位置
scrollBar.style.top = y + 'px';
// 根据滑块位置计算出内容区应该滚动的距离
let contentHeight = document.querySelector('.scroll-content').offsetHeight;
let scrollDistance = y / (trackHeight - scrollBar.offsetHeight) * (contentHeight - trackHeight);
// 将内容区移动到当前位置
document.querySelector('.scroll-content').scrollTop = scrollDistance;
}
function onMouseUp() {
// 移除事件绑定
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
}
});
let scrollTrack = document.querySelector('.scroll-track');
scrollTrack.addEventListener('click', (e) => {
// 计算出滑块的新位置
let trackHeight = scrollTrack.offsetHeight;
let barHeight = scrollBar.offsetHeight;
let y = e.clientY - scrollTrack.getBoundingClientRect().top - barHeight / 2;
if (y < 0) {
y = 0;
} else if (y > trackHeight - barHeight) {
y = trackHeight - barHeight;
}
// 将滑块移动到新位置
scrollBar.style.top = y + 'px';
// 根据滑块位置计算出内容区应该滚动的距离
let contentHeight = document.querySelector('.scroll-content').offsetHeight;
let scrollDistance = y / (trackHeight - barHeight) * (contentHeight - trackHeight);
// 将内容区移动到当前位置
document.querySelector('.scroll-content').scrollTop = scrollDistance;
});
let scrollWrapper = document.querySelector('.scroll-wrapper');
let scrollContent = document.querySelector('.scroll-content');
let mutationObserver = new MutationObserver((mutations) => {
// 计算滑块长度
let trackHeight = scrollWrapper.offsetHeight;
let barHeight = Math.pow(trackHeight, 2) / scrollContent.scrollHeight;
if (barHeight < 50) {
barHeight = 50;
}
// 更新滑块长度
scrollBar.style.height = barHeight + 'px';
});
mutationObserver.observe(scrollContent, {
childList: true,
subtree: true,
characterData: true,
attributes: true
});
</script>
示例2:动态添加滚动动态内容
```html
滚动条中的动态内容。