阅读量:0
实现效果如图所示,可以调整滚动的快慢,可以实现内容高度不够的时候停止滚动的效果。
1.实现原理:
创建一个dom为ul,赋值为当前列表数据,然后拷贝这个dom赋值给第二个ul,然后判断屏幕高度跟滚动高度对比,利用requestAnimationFrame动画实现滚动
2.注意事项:
这是基于react的滚动列表改造过来的,所以本身留了render口子,可以自定义表内的内容,不只是文字,但是对于vue3跟jsx的结合,我还是不是很熟,所以目前还没有用到render写法
3.对比:
最开始实现我是想用vue3-seamless-scroll这个插件的,但是使用起来发现,首先它不支持数据少的情况下自动停止滚动,需要传入step为0,感觉不是很方便(也可能是我没有很会用这个插件),所以感觉直接写了一个。
组件代码:
<template> <div class="scrollContainer" :key="currentTime" :style="{ height: `${height}px` }" > <div class="scrollHead" :style="{ height: headerHeight + 'px', }" > <div v-for="l in columns" :key="l.key" :style="{ width: `${l.width}px` }" > {{ l.title }} </div> </div> <ul class="scrollBody" ref="wrapperDom" :style="{ height: `${height - headerHeight}px` }" > <ul ref="childDom1" @mouseenter="handleEnter" @mouseleave="handleLeave" > <li v-for="(l, i) in dataSource" :data-key="rowKey ? l[rowKey] : `list${i}`" :key="rowKey ? l[rowKey] : `list${i}`" :style="{ height: `${rowHeight}px` }" > <div v-for="(p, c) in columns" :key="`p${c}`" :style="getStyle(p, l)" @click=" (e) => { e.stopPropagation() onCellClick(l, p) onRowClick?.(l) } " > {{ p?.render?.(i, l, l[p.key]) || l[p.key] }} </div> </li> </ul> <ul ref="childDom2"></ul> </ul> </div> </template> <script setup lang="ts"> import { onMounted, watch, ref, onBeforeUnmount, computed, nextTick } from 'vue' interface ViewProps { height: number dataSource: Record<string, any>[] columns: TableColumn[] headerHeight?: number rowHeight?: number onRowClick?: (l: Record<string, any>) => void rowKey?: string scroll?: boolean } export interface TableColumn { key: string title: string width: number render?: (index: number, data: Record<string, any>, text: any) => any onClick?: (data: Record<string, any>) => void } const props = defineProps<ViewProps>() const { height, columns, rowHeight = 27.5, headerHeight = 36, rowKey } = props const wrapperDom = ref<any>() const childDom1 = ref<any>() const childDom2 = ref<any>() const currentTime = ref(new Date().getTime()) let count = 0 let reqAnimation: number onMounted(() => { nextTick(() => { reqAnimation = window.requestAnimationFrame(taskStart) }) }) onBeforeUnmount(() => { handleEnter() }) const dataSource = computed(() => { console.log('dataSource', dataSource) return props.dataSource }) watch( () => props.dataSource, () => { currentTime.value = new Date().getTime() } ) const getStyle = (p, l) => { let pStyle = { width: `${p.width}px` } if (l.lineColor) { pStyle['color'] = l.lineColor } return pStyle } var startTime = null, stepInMs = 100, drawCount = 0 const taskStart = (timestamp: any) => { var progress if (startTime === null) { startTime = timestamp } progress = timestamp - startTime! if (progress > stepInMs) { startTime = timestamp if ( childDom1.value?.clientHeight >= wrapperDom.value?.clientHeight && childDom2.value?.clientHeight < 10 ) { childDom2.value.innerHTML = childDom1.value.innerHTML } if (wrapperDom.value?.scrollTop >= childDom1.value?.scrollHeight) { wrapperDom.value.scrollTop = 0 count = 0 } else { count += 1 wrapperDom.value.scrollTop = count } } if (props.scroll) { reqAnimation = window.requestAnimationFrame(taskStart) } } const handleEnter = () => { window.cancelAnimationFrame(reqAnimation) } const handleLeave = () => { reqAnimation = window.requestAnimationFrame(taskStart) } const onCellClick = (l: Record<string, any>, p: TableColumn) => { p?.onClick?.(l) } </script> <style lang="scss" scoped> .scrollContainer { width: 100%; div { text-align: center; display: inline-block; margin: 0; font-size: 14px; font-weight: normal; font-stretch: normal; letter-spacing: 0; opacity: 0.9; } .scrollHead { display: flex; align-items: center; background-color: rgba(33, 60, 93, 0.55); div { font-size: 14px; font-stretch: normal; letter-spacing: 0; font-family: MicrosoftYaHei, sans-serif; font-weight: bold; color: #ffffff; opacity: 0.47; } } .scrollBody { overflow-y: scroll; width: 100%; padding: 0; scrollbar-width: none; -ms-overflow-style: none; ul { height: auto; padding: 0; margin: 0; } li { list-style: none; position: relative; cursor: pointer; display: flex; height: 36px; color: #fff; align-items: center; } li div { line-height: 36px; color: #24acef; white-space: nowrap; /* 文本不换行 */ overflow: hidden; /* 溢出部分隐藏 */ text-overflow: ellipsis; /* 溢出部分用"..."代替 */ } li:hover { background: rgba(43, 143, 171, 0.52); > div { color: #fff; } } &::-webkit-scrollbar { display: none; } li:nth-child(even) { background-color: rgba(43, 143, 171, 0.13); } li:nth-child(even):hover { background: rgba(43, 143, 171, 0.52); color: #fff; } } } </style>
父组件调用:
<template> <div> <ScrollTable :height="300" :dataSource="dataSource" :columns="columns" :scroll="true" ></ScrollTable> </div> </template> <script setup lang="ts"> import { ref, reactive } from 'vue' import ScrollTable from '../../components/ScrollTable.vue' const dataSource = [ { name: '张三', age: 18, gender: '男', address: '北京市', phone: '12345678901', }, { name: '李四', age: 20, gender: '女', address: '上海市', phone: '12345678902', }, { name: '王五', age: 22, gender: '男', address: '广州市', phone: '12345678903', }, { name: '赵六', age: 22, gender: '男', address: '广州市', phone: '12345678903', }, { name: '王思聪', age: 22, gender: '男', address: '广州市', phone: '12345678903', }, { name: '王健林', age: 22, gender: '男', address: '广州市', phone: '12345678903', }, { name: '马云', age: 22, gender: '男', address: '广州市', phone: '12345678903', }, { name: '马化腾', age: 22, gender: '男', address: '广州市', phone: '12345678903', }, { name: '马1', age: 22, gender: '男', address: '广州市', phone: '12345678903', }, { name: '马2', age: 22, gender: '男', address: '广州市', phone: '12345678903', }, { name: '马3', age: 22, gender: '男', address: '广州市', phone: '12345678903', }, { name: '马4', age: 22, gender: '男', address: '广州市', phone: '12345678903', }, { name: '马5', age: 22, gender: '男', address: '广州市', phone: '12345678903', }, { name: '马6', age: 22, gender: '男', address: '广州市', phone: '12345678903', }, { name: '马7', age: 22, gender: '男', address: '广州市', phone: '12345678903', }, { name: '马8', age: 22, gender: '男', address: '广州市', phone: '12345678903', }, { name: '马9', age: 22, gender: '男', address: '广州市', phone: '12345678903', }, ] const columns = [ { title: '姓名', dataIndex: 'name', width: 50, key: 'name', }, { title: '年龄', dataIndex: 'age', width: 50, key: 'age', }, { title: '性别', dataIndex: 'gender', width: 50, key: 'gender', }, { title: '地址', dataIndex: 'address', width: 50, key: 'address', }, { title: '电话', dataIndex: 'phone', width: 50, key: 'phone', }, ] </script> <style lang="scss" scoped></style>