前言
我们在 Element-UI 的其他组件里发现,有使用这个组件,并且还不错,由于 Element-UI 官方文档并没有写到关于 el-scrollbar
的文档,所以稳定性上可能是有些问题。
于是只好自己写了一个类似于 el-scrollbar
功能的组件。
示例:
首先,需要创建一个 Vue 组件,该组件将包含包裹容器、内容容器和滚动条,然后监听滚动事件并处理用户交互。
<template>
<div class="el-scrollbar">
<div class="el-scrollbar__wrap" ref="wrap" @scroll="handleScroll">
<div class="el-scrollbar__content" :style="{ 'margin-right': verticalScrollbarWidth + 'px' }">
<!-- Your content goes here -->
</div>
</div>
<div class="el-scrollbar__bar" v-show="vBarVisible" ref="bar" @mousedown="startDrag">
<div class="el-scrollbar__thumb" ref="thumb" :style="thumbStyle"></div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
size: 0,
verticalScrollbarWidth: 0,
isDragging: false,
startY: 0,
scrollTop: 0,
thumbTop: 0,
};
},
computed: {
vBarVisible() {
const content = this.$refs.wrap;
if (content) {
return content.scrollHeight > content.clientHeight;
}
return false;
},
thumbStyle() {
return {
height: `${this.size}px`,
top: `${this.thumbTop}px`,
};
},
},
methods: {
handleScroll() {
this.scrollTop = this.$refs.wrap.scrollTop;
this.updateThumbStyle();
},
updateThumbStyle() {
const heightPercentage = (this.scrollTop / this.$refs.wrap.scrollHeight) * 100;
this.size = (this.$refs.wrap.clientHeight / this.$refs.wrap.scrollHeight) * 100;
this.thumbTop = (this.$refs.wrap.clientHeight - this.size) * (heightPercentage / 100);
},
startDrag(e) {
this.isDragging = true;
this.startY = e.clientY;
this.startScrollTop = this.scrollTop;
this.$refs.thumb.classList.add('el-scrollbar__thumb--hover');
document.addEventListener('mousemove', this.handleDrag);
document.addEventListener('mouseup', this.endDrag);
document.addEventListener('mouseleave', this.endDrag);
},
handleDrag(e) {
const deltaY = e.clientY - this.startY;
const percentage = deltaY / this.$refs.wrap.clientHeight;
this.$refs.wrap.scrollTop = this.startScrollTop + percentage * this.$refs.wrap.scrollHeight;
},
endDrag() {
this.isDragging = false;
this.$refs.thumb.classList.remove('el-scrollbar__thumb--hover');
document.removeEventListener('mousemove', this.handleDrag);
document.removeEventListener('mouseup', this.endDrag);
document.removeEventListener('mouseleave', this.endDrag);
},
},
created() {
this.verticalScrollbarWidth = this.calculateVerticalScrollbarWidth();
},
mounted() {
this.handleScroll();
},
updated() {
this.handleScroll();
},
methods: {
calculateVerticalScrollbarWidth() {
const outer = document.createElement('div');
outer.style.visibility = 'hidden';
outer.style.width = '100px';
outer.style.position = 'absolute';
outer.style.top = '-9999px';
document.body.appendChild(outer);
const widthNoScroll = outer.offsetWidth;
outer.style.overflow = 'scroll';
const inner = document.createElement('div');
inner.style.width = '100%';
outer.appendChild(inner);
const widthWithScroll = inner.offsetWidth;
outer.parentNode.removeChild(outer);
return widthNoScroll - widthWithScroll;
},
},
};
</script>
<style scoped>
.el-scrollbar {
position: relative;
overflow: hidden;
}
.el-scrollbar__wrap {
overflow: hidden;
position: relative;
}
.el-scrollbar__content {
margin-right: 0;
}
.el-scrollbar__bar {
position: absolute;
width: 6px;
right: 2px;
background: #e2e2e2;
border-radius: 4px;
transition: background-color 0.3s, width 0.3s;
}
.el-scrollbar__bar:hover {
background: #c0c0c0;
}
.el-scrollbar__thumb {
width: 6px;
height: 0;
background: #333;
border-radius: 4px;
transition: background-color 0.3s, height 0.3s;
}
.el-scrollbar__thumb.el-scrollbar__thumb--hover {
background: #000;
}
</style>
这里创建了一个包裹容器(custom-scrollbar)内包含滚动容器(scroll-wrapper)、内容容器(scroll-content)和滚动条(scroll-bar)。滚动条可以被拖动,滚动事件会更新滚动位置。