欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 新车 > Vue3滚动条(Scrollbar)

Vue3滚动条(Scrollbar)

2024/10/25 2:18:42 来源:https://blog.csdn.net/Dandrose/article/details/140526564  浏览:    关键词:Vue3滚动条(Scrollbar)

效果如下图:在线预览

在这里插入图片描述
在这里插入图片描述

APIs

Scrollbar

参数说明类型默认值必传
contentStyle内容样式CSSProperties{}false
size滚动条的大小,单位 pxnumber5false
trigger显示滚动条的时机,'none' 表示一直显示‘hover’ | ‘none’‘hover’false
horizontal是否使用横向滚动booleanfalsefalse

Methods

名称说明类型
scrollTo滚动内容(options: { left?: number, top?: number, behavior?: ScrollBehavior }): void & (x: number, y: number) => void
scrollBy滚动特定距离(options: { left?: number, top?: number, behavior?: ScrollBehavior }): void & (x: number, y: number) => void

ScrollBehavior Type

名称说明
smooth平滑滚动并产生过渡效果
instant滚动会直接跳转到目标位置,没有过渡效果
auto或缺省值表示浏览器会自动选择滚动时的过渡效果

Events

名称说明类型
scroll滚动的回调(e: Event) => void

创建滚动条组件Scrollbar.vue

<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import type { CSSProperties } from 'vue'
import { useEventListener, useMutationObserver } from '../utils'
interface Props {contentStyle?: CSSProperties // 内容样式size?: number // 滚动条的大小,单位 pxtrigger?: 'hover' | 'none' // 显示滚动条的时机,'none' 表示一直显示horizontal?: boolean // 是否使用横向滚动
}
const props = withDefaults(defineProps<Props>(), {contentStyle: () => ({}),size: 5,trigger: 'hover',horizontal: false
})
const scrollbarRef = ref()
const containerRef = ref()
const contentRef = ref()
const railVerticalRef = ref()
const railHorizontalRef = ref()
const showTrack = ref(false)
const containerScrollHeight = ref(0) // 滚动区域高度,包括溢出高度
const containerScrollWidth = ref(0) // 滚动区域宽度,包括溢出宽度
const containerClientHeight = ref(0) // 滚动区域高度,不包括溢出高度
const containerClientWidth = ref(0) // 滚动区域宽度,不包括溢出宽度
const containerHeight = ref(0) // 容器高度
const containerWidth = ref(0) // 容器宽度
const contentHeight = ref(0) // 内容高度
const contentWidth = ref(0) // 内容宽度
const railHeight = ref(0) // 滚动条高度
const railWidth = ref(0) // 滚动条宽度
const containerScrollTop = ref(0) // 垂直滚动距离
const containerScrollLeft = ref(0) // 水平滚动距离
const trackYPressed = ref(false) // 垂直滚动条是否被按下
const trackXPressed = ref(false) // 水平滚动条是否被按下
const mouseLeave = ref(false) // 鼠标在按下滚动条并拖动时是否离开滚动区域
const memoYTop = ref<number>(0) // 鼠标选中并按下垂直滚动条时已滚动的垂直距离
const memoXLeft = ref<number>(0) // 鼠标选中并按下水平滚动条时已滚动的水平距离
const memoMouseY = ref<number>(0) // 鼠标选中并按下垂直滚动条时的鼠标 Y 坐标
const memoMouseX = ref<number>(0) // 鼠标选中并按下水平滚动条时的鼠标 X 坐标
const horizontalContentStyle = { width: 'fit-content' } // 水平滚动时内容区域默认样式
const emit = defineEmits(['scroll'])
const isYScroll = computed(() => {// 是否存在垂直滚动return containerScrollHeight.value > containerClientHeight.value
})
const isXScroll = computed(() => {// 是否存在水平滚动return containerScrollWidth.value > containerClientWidth.value
})
const isScroll = computed(() => {// 是否存在滚动,水平或垂直return isYScroll.value || (props.horizontal && isXScroll.value)
})
const trackHeight = computed(() => {// 垂直滚动条高度if (isYScroll.value) {if (containerHeight.value && contentHeight.value && railHeight.value) {const value = Math.min(containerHeight.value,(railHeight.value * containerHeight.value) / contentHeight.value + 1.5 * props.size)return Number(value.toFixed(4))}}return 0
})
const trackTop = computed(() => {// 滚动条垂直偏移if (containerHeight.value && contentHeight.value && railHeight.value) {return ((containerScrollTop.value / (contentHeight.value - containerHeight.value)) *(railHeight.value - trackHeight.value))}return 0
})
const trackWidth = computed(() => {// 横向滚动条宽度if (props.horizontal && isXScroll.value) {if (containerWidth.value && contentWidth.value && railWidth.value) {const value = (railWidth.value * containerWidth.value) / contentWidth.value + 1.5 * props.sizereturn Number(value.toFixed(4))}}return 0
})
const trackLeft = computed(() => {// 滚动条水平偏移if (containerWidth.value && contentWidth.value && railWidth.value) {return ((containerScrollLeft.value / (contentWidth.value - containerWidth.value)) * (railWidth.value - trackWidth.value))}return 0
})
onMounted(() => {updateState()
})
function updateScrollState() {containerScrollTop.value = containerRef.value.scrollTopcontainerScrollLeft.value = containerRef.value.scrollLeft
}
function updateScrollbarState() {containerScrollHeight.value = containerRef.value.scrollHeightcontainerScrollWidth.value = containerRef.value.scrollWidthcontainerClientHeight.value = containerRef.value.clientHeightcontainerClientWidth.value = containerRef.value.clientWidthcontainerHeight.value = containerRef.value.offsetHeightcontainerWidth.value = containerRef.value.offsetWidthcontentHeight.value = contentRef.value.offsetHeightcontentWidth.value = contentRef.value.offsetWidthrailHeight.value = railVerticalRef.value.offsetHeightrailWidth.value = railHorizontalRef.value.offsetWidth
}
function updateState() {updateScrollState()updateScrollbarState()
}
useEventListener(window, 'resize', updateState)
const options = { childList: true, attributes: true, subtree: true }
useMutationObserver(scrollbarRef, updateState, options)
function onScroll(e: Event) {emit('scroll', e)updateScrollState()
}
function onMouseEnter() {if (props.horizontal) {if (trackXPressed.value) {mouseLeave.value = false} else {showTrack.value = true}} else {if (trackYPressed.value) {mouseLeave.value = false} else {showTrack.value = true}}
}
function onMouseLeave() {if (props.horizontal) {if (trackXPressed.value) {mouseLeave.value = true} else {showTrack.value = false}} else {if (trackYPressed.value) {mouseLeave.value = true} else {showTrack.value = false}}
}
function onTrackVerticalMouseDown(e: MouseEvent) {trackYPressed.value = truememoYTop.value = containerScrollTop.valuememoMouseY.value = e.clientYwindow.onmousemove = (e: MouseEvent) => {const diffY = e.clientY - memoMouseY.valueconst dScrollTop =(diffY * (contentHeight.value - containerHeight.value)) / (containerHeight.value - trackHeight.value)const toScrollTopUpperBound = contentHeight.value - containerHeight.valuelet toScrollTop = memoYTop.value + dScrollToptoScrollTop = Math.min(toScrollTopUpperBound, toScrollTop)toScrollTop = Math.max(toScrollTop, 0)containerRef.value.scrollTop = toScrollTop}window.onmouseup = () => {window.onmousemove = nulltrackYPressed.value = falseif (props.trigger === 'hover' && mouseLeave.value) {showTrack.value = falsemouseLeave.value = false}}
}
function onTrackHorizontalMouseDown(e: MouseEvent) {trackXPressed.value = truememoXLeft.value = containerScrollLeft.valuememoMouseX.value = e.clientXwindow.onmousemove = (e: MouseEvent) => {const diffX = e.clientX - memoMouseX.valueconst dScrollLeft =(diffX * (contentWidth.value - containerWidth.value)) / (containerWidth.value - trackWidth.value)const toScrollLeftUpperBound = contentWidth.value - containerWidth.valuelet toScrollLeft = memoXLeft.value + dScrollLefttoScrollLeft = Math.min(toScrollLeftUpperBound, toScrollLeft)toScrollLeft = Math.max(toScrollLeft, 0)containerRef.value.scrollLeft = toScrollLeft}window.onmouseup = () => {window.onmousemove = nulltrackXPressed.value = falseif (props.trigger === 'hover' && mouseLeave.value) {showTrack.value = falsemouseLeave.value = false}}
}
function scrollTo(...args: any[]) {containerRef.value?.scrollTo(...args)
}
function scrollBy(...args: any[]) {containerRef.value?.scrollBy(...args)
}defineExpose({scrollTo,scrollBy
})
</script>
<template><divref="scrollbarRef"class="m-scrollbar":style="`--scrollbar-size: ${size}px;`"@mouseenter="isScroll && trigger === 'hover' ? onMouseEnter() : () => false"@mouseleave="isScroll && trigger === 'hover' ? onMouseLeave() : () => false"><div ref="containerRef" class="m-scrollbar-container" @scroll="onScroll"><divref="contentRef"class="m-scrollbar-content":style="[horizontal ? { ...horizontalContentStyle, ...contentStyle } : contentStyle]"><slot></slot></div></div><div ref="railVerticalRef" class="m-scrollbar-rail rail-vertical"><Transition name="fade"><divv-if="trigger === 'none' || showTrack"class="m-scrollbar-track":style="`top: ${trackTop}px; height: ${trackHeight}px;`"@mousedown.prevent.stop="onTrackVerticalMouseDown"></div></Transition></div><div ref="railHorizontalRef" v-show="horizontal" class="m-scrollbar-rail rail-horizontal"><Transition name="fade"><divv-if="trigger === 'none' || showTrack"class="m-scrollbar-track":style="`left: ${trackLeft}px; width: ${trackWidth}px;`"@mousedown.prevent.stop="onTrackHorizontalMouseDown"></div></Transition></div></div>
</template>
<style lang="less" scoped>
.fade-enter-active,
.fade-leave-active {transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1) !important;
}
.fade-enter-from,
.fade-leave-to {opacity: 0;
}
.m-scrollbar {overflow: hidden;position: relative;z-index: auto;height: 100%;width: 100%;.m-scrollbar-container {width: 100%;overflow: scroll;height: 100%;min-height: inherit;max-height: inherit;scrollbar-width: none;&::-webkit-scrollbar,&::-webkit-scrollbar-track-piece,&::-webkit-scrollbar-thumb {width: 0;height: 0;display: none;}.m-scrollbar-content {box-sizing: border-box;min-width: 100%;}}.m-scrollbar-rail {position: absolute;pointer-events: none;user-select: none;background: transparent;-webkit-user-select: none;.m-scrollbar-track {z-index: 1;position: absolute;cursor: pointer;pointer-events: all;background-color: rgba(0, 0, 0, 0.25);transition: background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1);&:hover {background-color: rgba(0, 0, 0, 0.4);}}}.rail-vertical {inset: 2px 4px 2px auto;width: var(--scrollbar-size);.m-scrollbar-track {width: var(--scrollbar-size);border-radius: var(--scrollbar-size);bottom: 0;}}.rail-horizontal {inset: auto 2px 4px 2px;height: var(--scrollbar-size);.m-scrollbar-track {height: var(--scrollbar-size);border-radius: var(--scrollbar-size);right: 0;}}
}
</style>

在要使用的页面引入

<script setup lang="ts">
import Scrollbar from './Scrollbar.vue'
function onScroll(e: Event) {console.log('scroll:', e)
}
</script>
<template><div><h1>{{ $route.name }} {{ $route.meta.title }}</h1><h2 class="mt30 mb10">基本使用</h2><Scrollbar style="max-height: 120px" @scroll="onScroll">我们在田野上面找猪<br />想象中已找到了三只<br />小鸟在白云上面追逐<br />它们在树底下跳舞<br />啦啦啦啦啦啦啦啦咧<br />啦啦啦啦咧<br />我们在想象中度过了许多年<br />想象中我们是如此的疯狂<br />我们在城市里面找猪<br />想象中已找到了几百万只<br />小鸟在公园里面唱歌<br />它们独自在想象里跳舞<br />啦啦啦啦啦啦啦啦咧<br />啦啦啦啦咧<br />我们在想象中度过了许多年<br />许多年之后我们又开始想象<br />啦啦啦啦啦啦啦啦咧</Scrollbar><h2 class="mt30 mb10">横向滚动</h2><Scrollbar horizontal><div style="white-space: nowrap; padding: 12px">我们在田野上面找猪 想象中已找到了三只 小鸟在白云上面追逐 它们在树底下跳舞 啦啦啦啦啦啦啦啦咧 啦啦啦啦咧我们在想象中度过了许多年 想象中我们是如此的疯狂 我们在城市里面找猪 想象中已找到了几百万只 小鸟在公园里面唱歌它们独自在想象里跳舞 啦啦啦啦啦啦啦啦咧 啦啦啦啦咧 我们在想象中度过了许多年 许多年之后我们又开始想象啦啦啦啦啦啦啦啦咧</div></Scrollbar><h2 class="mt30 mb10">触发方式</h2><Scrollbar horizontal style="max-height: 120px" trigger="none">我们在田野上面找猪<br />想象中已找到了三只<br />小鸟在白云上面追逐<br />它们在树底下跳舞<br />啦啦啦啦啦啦啦啦咧<br />啦啦啦啦咧<br />我们在想象中度过了许多年<br />想象中我们是如此的疯狂<br />我们在城市里面找猪<br />想象中已找到了几百万只<br />小鸟在公园里面唱歌<br />它们独自在想象里跳舞<br />啦啦啦啦啦啦啦啦咧<br />啦啦啦啦咧<br />我们在想象中度过了许多年<br />许多年之后我们又开始想象<br />啦啦啦啦啦啦啦啦咧</Scrollbar><h2 class="mt30 mb10">自定义内容样式</h2><Scrollbarstyle="max-height: 120px; border-radius: 12px":content-style="{ backgroundColor: '#e6f4ff', padding: '16px 24px', fontSize: '16px' }">我们在田野上面找猪<br />想象中已找到了三只<br />小鸟在白云上面追逐<br />它们在树底下跳舞<br />啦啦啦啦啦啦啦啦咧<br />啦啦啦啦咧<br />我们在想象中度过了许多年<br />想象中我们是如此的疯狂<br />我们在城市里面找猪<br />想象中已找到了几百万只<br />小鸟在公园里面唱歌<br />它们独自在想象里跳舞<br />啦啦啦啦啦啦啦啦咧<br />啦啦啦啦咧<br />我们在想象中度过了许多年<br />许多年之后我们又开始想象<br />啦啦啦啦啦啦啦啦咧</Scrollbar></div>
</template>

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com