个人项目地址:SubTopH前端开发个人站
(自己开发的前端功能和UI组件,一些有趣的小功能,感兴趣的伙伴可以访问,欢迎提出更好的想法,私信沟通,网站属于静态页面)
SubTopH前端开发个人站https://subtop.gitee.io/subtoph.github.io/#/home
以上 👆 是个人前端项目,欢迎提出您的建议😊
以下是正文内容...............
实现功能:
动态监听元素高度变化,自动更新滚动条动态监听浏览器窗口尺寸,自动更新滚动条可配置,滚动条颜色,宽度,高度可监听滚动条触底或顶部事件
创建scrollContainer.vue组件
<template><divclass="scrollbar-box":class="{ hide: winWidth < 500 }":style="{ height: height }"><div class="scrollbar-y"><div ref="scrollRef" class="scroll-wrap" @scroll="onMosewheel"><slot></slot></div><divref="barRef"class="scrollbar-track":style="{backgroundColor: heightPre == 1 ? 'rgba(0,0,0,0)' : slotColor}"><!--没有滑块方式,背景色透明:使用v-show窗口变大,滑块不出现,所以使用背景色控制backgroundColor: heightPre == 1 ? 'rgba(0,0,0,0)' : scrollColor,--><div:style="{height: barHeight + 'px',width: scrollWidth + 'px',transform: 'translateY(' + translateY + 'px)',backgroundColor: heightPre == 1 ? 'rgba(0,0,0,0)' : scrollColor}"class="scrollbar-thumb"@mousedown.stop.prevent="moveStart"></div></div></div></div></template><script>import {defineComponent,toRefs,onMounted,nextTick,reactive,onUnmounted} from 'vue'export default defineComponent({name: 'scrollContainer',props: {height: {//:height="'200px'"type: String,default: '100%'},slotColor: {//滑块槽颜色 :slotColor="'red'"type: String,default: 'rgba(0,0,0,0)'},scrollColor: {//滑块颜色 :scrollColor="'red'"type: String,default: '#999'},scrollWidth: {//滑块宽度 :scrollWidth="7"type: Number,default: 12}},setup(props, ctx) {const data = reactive({scrollRef: null, // 内容盒子barRef: null, // 滚动条轨道translateY: 0, // 滚动块平移的距离heightPre: 0, // 可视高度和内容高度比barHeight: 0, // 滑块高度winWidth: document.body.clientWidth //初始化浏览器页面宽度})let time = null // 定时器let isMove = false // 判断鼠标是否点击滑块(为松开)let moveClientY = 0 // 鼠标点击滑块时,相对滑块的位置let trackHeight = 0 // 滚动条轨道高度let wrapHeight = 0 // 容器高度(可视高度)let wrapContentHeight = 0 // 内容高度(可滚动内容的高度)// 页面挂载后计算滚动条onMounted(() => {monitorWindow() //监听窗口尺寸monitorScrollBar() //监听内容元素尺寸nextTick(() => {//dom渲染后calculationLength() //初始化延迟更新滚动条})})// 页面卸载清除定时器onUnmounted(() => {window.clearInterval(time)time = null})// 监听页面尺寸改变计算滚动条const monitorWindow = function () {let time //定时器,防抖,窗口持续变化,延迟更新滚动条window.addEventListener('resize', () => {data.winWidth = document.body.clientWidth //页面改变监听宽度控制移动端隐藏滚动条clearTimeout(time)time = setTimeout(() => {//页面宽度变化继续监听,如果小于500就关闭自定义滚动条// console.log("浏览器窗口变化更新滚动条");initScrollListner()}, 500)})}//监听内容元素尺寸变化const monitorScrollBar = function () {var monitorUl = data.scrollRef.children[0]// var monitorDiv= document; // 监听documentlet MutationObserver =window.MutationObserver ||window.WebKitMutationObserver ||window.MozMutationObserverlet observer = new MutationObserver(function (mutations) {// console.log("内容元素变化更新滚动条");initScrollListner()})// childList 观察子节点变动// 监听子节点增加或者内容撑起的尺寸observer.observe(monitorUl, {attributes: true,childList: true// subtree: true, //监听子元素内容变化})// 监听document// observer.observe(monitorDiv, {// childList:true,// subtree:true,// });}// 初始化延迟监听滚动条const calculationLength = function () {// console.log("初始化页面更新滚动条");// 直接执行initScrollListner函数,获取滚动条长度部准确// 因为页面渲染有延迟,获取dom元素需要延迟// 每间隔10毫秒更新滑块长度time = setInterval(() => {// 计算滚动条长度initScrollListner()}, 50)// 间隔500毫秒清除定时器,滑块缩短会有动画效果,时间可延长没有影响setTimeout(() => {window.clearInterval(time)time = null}, 2000)}// 计算滚动条高度const initScrollListner = function () {let scroll = data.scrollReflet bar = data.barRef// scroll有时候拿不到元素,要判断一下if (scroll) {wrapContentHeight = scroll.scrollHeightwrapHeight = scroll.clientHeighttrackHeight = bar.clientHeight// console.log(wrapContentHeight ,wrapHeight);// 容器高度 / 内容高度 100 150data.heightPre = wrapHeight / wrapContentHeight// 滑动块的高度 根据 容器和内容的比 乘以 滚动轨道 计算出 滑动块的高度data.barHeight = data.heightPre * trackHeight}}// 内容滚动时,计算滑块移动的距离const onMosewheel = function (e) {// scrollTop页面顶部滚出的高度// offsetHeight页面可视区域高度// scrollHeight页面正文全文高度// data.translateY滚动块平移的距离data.translateY = e.target.scrollTop * data.heightPreif (data.translateY == 0) {// 到达顶部arrive('top')} else if (e.target.scrollTop + e.target.offsetHeight ==e.target.scrollHeight) {// 滚出高度 + 可视区域高度 == 内容高度arrive('bottom')}}// 到达顶部或者底部通知父级元素const arrive = function name(tb) {ctx.emit('arrive', tb)}// 鼠标点击滑块时const moveStart = function (e) {isMove = true// clientY:当鼠标事件发生时,鼠标相对于浏览器(这里说的是浏览器的有效区域)y轴的位置// data.translateY 滚动块平移的距离// moveClientY 鼠标点击滑块时,相对滑块的位置moveClientY = e.clientY - data.translateYmoveTo() //移动时moveEnd() //鼠标松开时}// 鼠标移动,改变thumb的位置以及容器scrollTop的位置const moveTo = function () {document.onmousemove = (e) => {// 移动时候判断是不是松开,松开就不在执行滑块移动操作if (isMove) {// 移动滑块时,判断时候到达顶部或者底部if (e.clientY - moveClientY > trackHeight - data.barHeight) {// 滑块到达 底部 就不在改变滑块translateY值data.translateY = trackHeight - data.barHeight} else if (e.clientY - moveClientY < 0) {// 滑块到达 顶部 就不在改变滑块translateY值data.translateY = 0} else {//改变滑块位置data.translateY = e.clientY - moveClientY}// 计算出内容盒子滚出顶部的距离data.scrollRef.scrollTop = data.translateY / data.heightPre}}}// 鼠标从滑块松开时,不在监听滑块移动操作const moveEnd = function () {document.onmouseup = (e) => {if (isMove) {isMove = false}}}return {...toRefs(data),onMosewheel,moveStart}}})</script><style lang="less" scoped>.scrollbar-box {height: 100%;overflow: hidden;}.scrollbar-y {position: relative;height: 100%;margin-right: -17px; //遮住浏览器自带滚动条.scroll-wrap {height: 100%;overflow-y: scroll;}.scrollbar-track {position: absolute;top: 0;right: 17px;bottom: 0;border-radius: 8px;z-index: 20;.scrollbar-thumb {margin: 0 auto;border-radius: 6px;cursor: default;}}}// 以下都注释,一直显示自定义滚动条//移动端隐藏自定义滚动条.hide.scrollbar-box .scrollbar-track {display: none;}//移动端显示原生滑块.hide.scrollbar-box .scrollbar-y {margin: 0;}</style>
使用时可以全局引入或者单独引入
<scrollContainer@arrive="arrive":height="'200px'":slotColor="'rgb(185, 227, 255)'":scrollColor="'#00f'"><ul v-for="item in 17" :key="item"><li>{{ item }} 信息列表</li></ul></scrollContainer><script>import ScrollContainer from '.scrollContainer.vue'export default defineComponent({components: {ScrollContainer,},setup() {// 滚动条触底或顶事件const arrive = function (tb) {console.log(tb)}return {article,}}})</script>
组件可自定义配置
@arrive为滚动触底或顶部事件
效果展示
如果觉得《(组件)vue3封装自定义滚动条》对你有帮助,请点赞、收藏,并留下你的观点哦!