vue3+elementPlus实现无缝滚动表格封装
vue3+elementPlus+css+js 模拟liMarquee插件,实现无限滚动效果
功能:1、表格数据大于一定数量之后,开始向上滚动
2、当鼠标移入的时候,动画停止,鼠标移出,继续动画
3、滚动动画的速度可以自定义
4、表格的高度固定
5、向上滚动时,无限滚动,不存在卡顿
<template>
<div
class="scrolling-table-container"
@mouseenter="pauseAnimation"
@mouseleave="resumeAnimation"
>
<div class="table-wrapper" :style="{ height: tableHeight }">
<el-table
:data="displayData"
:loading="loading"
style="width: 100%"
:show-header="showHeader"
:row-key="rowKey"
class="scrolling-table"
:class="{
'enable-scroll': needScroll,
'is-paused': isPaused
}"
>
<el-table-column
v-for="column in columns"
:key="column.prop"
:prop="column.prop"
:label="column.label"
:width="column.width"
:min-width="column.minWidth"
:fixed="column.fixed"
>
<template #default="scope" v-if="column.hasSlot">
<slot :name="column.prop" :row="scope.row" :index="scope.$index"></slot>
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script setup lang="ts" name="ScrollingTable">
import { ref, computed, watch, onMounted, onBeforeUnmount } from 'vue'
import { ElTable, ElTableColumn } from 'element-plus'
interface TableColumn {
prop: string
label: string
width?: string | number
minWidth?: string | number
fixed?: string | boolean
hasSlot?: boolean
}
interface ScrollingTableProps {
tableData: Record<string, unknown>[]
columns: TableColumn[]
loading?: boolean
rowKey?: string
showHeader?: boolean
tableHeight?: string
scrollThreshold?: number
scrollSpeed?: number
scrollDelay?: number
}
const props = withDefaults(defineProps<ScrollingTableProps>(), {
loading: false,
rowKey: 'id',
showHeader: true,
tableHeight: '250px',
scrollThreshold: 8,
scrollSpeed: 120,
scrollDelay: 2000
})
const displayData = ref<Record<string, unknown>[]>([])
const isPaused = ref(false)
let delayTimer: number | null = null
// 计算是否需要滚动
const needScroll = computed(() => props.tableData.length > props.scrollThreshold)
// 计算动画持续时间
const animationDuration = computed(() => {
if (!needScroll.value) return 0
// 基于数据长度计算合适的动画时间,确保平滑滚动
return Math.max(props.tableData.length * props.scrollSpeed, 3000)
})
// 初始化显示数据
const initDisplayData = () => {
if (needScroll.value && props.tableData.length > 0) {
// 为无缝滚动复制数据
displayData.value = [
...props.tableData,
...props.tableData
]
} else {
displayData.value = [...props.tableData]
}
}
// 启动动画(延迟)
const startAnimation = () => {
if (!needScroll.value) return
clearTimeout(delayTimer!)
delayTimer = setTimeout(() => {
}, props.scrollDelay)
}
// 停止所有定时器
const stopTimers = () => {
if (delayTimer) {
clearTimeout(delayTimer)
delayTimer = null
}
}
// 暂停动画
const pauseAnimation = () => {
isPaused.value = true
}
// 恢复动画
const resumeAnimation = () => {
isPaused.value = false
}
// 重新初始化
const reinitialize = () => {
stopTimers()
isPaused.value = false
initDisplayData()
startAnimation()
}
// 监听数据变化
watch(() => props.tableData, () => {
reinitialize()
}, { immediate: true })
// 监听配置变化
watch([
() => props.scrollThreshold,
() => props.scrollSpeed,
() => props.scrollDelay
], () => {
reinitialize()
})
onMounted(() => {
startAnimation()
})
onBeforeUnmount(() => {
stopTimers()
})
// 暴露方法
defineExpose({
reinitialize,
needScroll,
pauseAnimation,
resumeAnimation
})
</script>
<style lang="scss" scoped>
.scrolling-table-container {
cursor: default;
:deep(.el-table thead){
color: #242933 !important;
font-weight: 500;
}
.table-wrapper {
overflow: hidden;
position: relative;
:deep(.el-table thead th.el-table__cell) {
background: #F8F9FA !important;
}
}
.scrolling-table {
// 基本样式
:deep(.el-table__body-wrapper) {
overflow: hidden !important;
&::-webkit-scrollbar {
display: none;
}
}
:deep(.el-table__header-wrapper) {
overflow-x: hidden;
}
:deep(.el-table thead){
color: #242933 !important;
font-weight: 500;
}
:deep(.el-table__row) {
transition: none;
}
:deep(.el-table__body) {
transition: none;
}
// 鼠标悬停样式
:deep(.el-table__row):hover {
background-color: #f5f7fa;
}
// 启用滚动时的动画
&.enable-scroll :deep(.el-table__body) {
animation: scroll-up v-bind("animationDuration + 'ms'") linear infinite;
animation-delay: v-bind("scrollDelay + 'ms'");
}
// 暂停状态
&.is-paused :deep(.el-table__body) {
animation-play-state: paused;
}
}
}
// 滚动动画关键帧 - 无限向上滚动
@keyframes scroll-up {
0% {
transform: translateY(0);
}
100% {
transform: translateY(-50%);
}
}
// 响应式优化
@media (prefers-reduced-motion: reduce) {
.scrolling-table.enable-scroll :deep(.el-table__body) {
animation: none;
}
}
</style>
相比liMarquee,优点如下:
1、不用使用npm 安装liMarquee包
2、liMarquee是基于jquery的,使用liMarquee,还得安装jquery
3、使用js、css 实现动画效果,性能友好