前言

公司展示大屏需要写滚动表格,通过滚动播放数据,自己随便摸了一个基于动画的自动滚动表格

原理

根据每行的大小和设置的每行滚动时间设置滚动位置,动态添加动画,并把数组第一项移动到最后一项,并订阅该动画结束的事件,在结束时循环执行该操作。

其他功能

  • 可自定义单元格或行
  • 可设置中文映射和取消显示
  • 单元格默认基于网格的响应式大小
  • 鼠标进入时可设置暂停

代码

            
{{ props.titleMapping?.get(item) ?? item }}
{if(props.pauseWhenMouseEnter) animation?.pause()}" v-on:mouseleave="() => {if(props.pauseWhenMouseEnter) animation?.play()}" >
{{ Object.keys(item.data).includes(key) ? item.data[key] ? item.data[key] : props.undefinedPlaceholder : props.undefinedPlaceholder }} import BaseBox from "./BaseBox.vue";import { defineProps, withDefaults, onMounted, computed, ref, watch,} from "vue";const props = withDefaults( defineProps<{ // 属性名翻译为标题,默认值 属性名列表 titleMapping?: Map; // 列宽,与 grid-template-columns 格式,默认值 repeat(${props.displayTitles?.length ?? Object.keys(props.list[0]).length}, 1fr) columnSizes?: string; // 列表 list: Array; // 展示哪些标题,默认值 全部展示 displayTitles?: Array; // 走完每一行的时间,默认值 2300 ms interval?: number; // 是否显示标题行,默认值 true noTitle?: Boolean; // 属性无参数时替换为某字符串,默认值 -- undefinedPlaceholder?: string; // 鼠标进入时暂停,默认值 true pauseWhenMouseEnter?: Boolean; }>(), { interval: 2300, noTitle: false, undefinedPlaceholder: "--", pauseWhenMouseEnter: false, });const innerList = ref<Array>( props.list.map((item, index) => ({ id: index, data: item })));const container = ref();onMounted(() => { animate(true);});// 监控数据列表更新watch( () => props.list, () => { innerList.value = props.list.map((item, index) => ({ id: index, data: item, })); });// 计算列大小const columnSize = computed(() => { return ( props.columnSizes ?? `repeat(${ props.displayTitles?.length ?? Object.keys(props.list[0]).length }, 1fr)` );});// 进行动画const animation = ref();const animate = (isStart = false) => { // 计算动画高度 let height = 0; if (!isStart) { height = -container.value!.children[1].getBoundingClientRect().height; // 移动数组第一个到最后一个 let temp = innerList.value.shift(); innerList.value.push(temp!); } else { height = -container.value!.children[0].getBoundingClientRect().height; } // 进行动画 animation.value = container.value!.animate( [ { top: `${height}px`, }, ], { duration: props.interval, iterations: 1, } ); // 监听动画完成后,重新开始动画 animation.value.addEventListener("finish", () => animate(false));};.title-container { display: grid; padding: 1rem 0; font-size: 1.25rem; background-color: rgb(24, 34, 103); grid-template-columns: v-bind(columnSize); text-align: center;}:slotted(.item-container),.item-container { overflow: hidden; position: relative; left: 0; right: 0; top: 0; display: grid; padding: 1rem 0; grid-template-columns: v-bind(columnSize);}:slotted(.item),.item { text-align: center; font-size: 1.25rem;}.scroll-table { width: 100%; height: 100%; overflow: hidden; .container { overflow: hidden; position: relative; left: 0; right: 0; top: 0; }}

参考

  • 插槽 Slots | Vue.js
  • 使用 CSS 动画 – CSS:层叠样式表 | MDN