vue3很丝滑的table表格向上滚动效果,多用于统计页面

效果如图, 上边无缝滚动

组件代码:

xml 复制代码
<template>
  <div class="table-scroll-wrapper" ref="wrapperRef" @mouseenter="handleMouseEnter" @mouseleave="handleMouseLeave">
    <div class="table-scroll-content" ref="contentRef">
      <!-- 原始内容 -->
      <div ref="originalContentRef">
        <slot></slot>
      </div>
      <!-- 克隆内容用于无缝滚动 -->
      <div v-if="shouldScroll" ref="cloneContentRef">
        <slot></slot>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue';

const props = defineProps({
  // 滚动速度(像素/帧)
  speed: {
    type: Number,
    default: 0.5
  },
  // 鼠标悬停是否暂停
  hoverPause: {
    type: Boolean,
    default: true
  },
  // 是否启用滚动
  enabled: {
    type: Boolean,
    default: true
  }
});

// 引用DOM元素
const wrapperRef = ref<HTMLElement>();
const contentRef = ref<HTMLElement>();
const originalContentRef = ref<HTMLElement>();
const cloneContentRef = ref<HTMLElement>();

// 滚动状态
const animationId = ref<number>();
const currentTop = ref(0);
const isPaused = ref(false);
const contentHeight = ref(0);

// 计算属性:是否需要滚动
const shouldScroll = computed(() => {
  return props.enabled && contentHeight.value > (wrapperRef.value?.offsetHeight || 0);
});

// 滚动动画函数
const scroll = () => {
  if (!shouldScroll.value || isPaused.value || !wrapperRef.value || !contentRef.value) {
    return;
  }

  // 向上滚动
  currentTop.value -= props.speed;
  contentRef.value.style.transform = `translateY(${currentTop.value}px)`;

  // 当滚动过原始内容高度时,重置位置实现无缝滚动
  if (Math.abs(currentTop.value) >= contentHeight.value) {
    currentTop.value = 0;
  }

  // 继续下一帧动画
  animationId.value = requestAnimationFrame(scroll);
};

// 开始滚动
const startScroll = () => {
  if (animationId.value) {
    cancelAnimationFrame(animationId.value);
  }
  if (shouldScroll.value) {
    scroll();
  }
};

// 停止滚动
const stopScroll = () => {
  if (animationId.value) {
    cancelAnimationFrame(animationId.value);
    animationId.value = undefined;
  }
};

// 鼠标进入处理
const handleMouseEnter = () => {
  if (props.hoverPause) {
    isPaused.value = true;
    stopScroll();
  }
};

// 鼠标离开处理
const handleMouseLeave = () => {
  if (props.hoverPause) {
    isPaused.value = false;
    startScroll();
  }
};

// 更新内容高度
const updateContentHeight = () => {
  if (originalContentRef.value) {
    contentHeight.value = originalContentRef.value.offsetHeight;
  }
};

// 监听窗口大小变化
const handleResize = () => {
  updateContentHeight();
  // 如果条件变化,重新控制滚动
  if (shouldScroll.value && !isPaused.value && props.enabled) {
    startScroll();
  } else if (!shouldScroll.value) {
    stopScroll();
    currentTop.value = 0;
    if (contentRef.value) {
      contentRef.value.style.transform = 'translateY(0)';
    }
  }
};

// 生命周期钩子
onMounted(async () => {
  // 等待DOM完全渲染
  await nextTick();
  updateContentHeight();

  // 开始滚动
  if (shouldScroll.value && props.enabled) {
    startScroll();
  }

  // 监听窗口大小变化
  window.addEventListener('resize', handleResize);
});

onUnmounted(() => {
  stopScroll();
  window.removeEventListener('resize', handleResize);
});
</script>

<style scoped>
.table-scroll-wrapper {
  width: 100%;
  height: 100%;
  overflow: hidden;
  position: relative;
}

.table-scroll-content {
  width: 100%;
  position: absolute;
  top: 0;
  left: 0;
  will-change: transform;
}

/* 确保表格行正确显示 */
:deep(.el-row) {
  width: 100%;
  box-sizing: border-box;
}
</style>

页面中的使用

xml 复制代码
<template>
<div style="height: 20.3rem; overflow: hidden; margin-top: .4rem;">
    <simple-scroll :speed="0.5" :hoverPause="true" v-if="MaintainList && MaintainList.length > 0">
      <el-row class="screen_ltable_cont" :key="index" v-for="(item, index) in MaintainList" :gutter="10">
        <el-col :span="6">
          <el-tooltip class="box-item" effect="dark" :content="item.deviceAdress" placement="top">
            <div class="textellipsis">{{ index }}</div>
          </el-tooltip>
        </el-col>
        <el-col :span="5">
          <span class="screen_tag screen_warning" v-if="item.status == '1'">维修中</span>
          <span class="screen_tag screen_primary" v-if="item.status == '2'">维修完成</span>
        </el-col>
      </el-row>
    </simple-scroll>
    </div>
</template>
<script setup>
import SimpleScroll from '../mycomponents/simpleScroll/index.vue';
</script>
相关推荐
excel7 小时前
为什么需要构建工具(Webpack / Vite 的本质)
前端
lang201509287 小时前
Java SAX 流式解析全解:从原理到 EasyExcel 实战
java·前端·javascript
Rain5097 小时前
2.4. PostgreSQL 数据库连接与实战指南
前端·数据库·人工智能·后端·postgresql·数据分析
console.log('npc')7 小时前
Codex 桌面端接入 Headroom 压缩代理完整教程
前端·vscode
独泪了无痕7 小时前
Vue集成uuid生成唯一标识实践指南
前端·vue.js
yuanyxh15 小时前
Mac 软件推荐
前端·javascript·程序员
万少15 小时前
AtomCode开发微信小程序《谁去呀》 全流程
前端·javascript·后端
某人辛木16 小时前
Web自动化测试
前端·python·pycharm·pytest
Kagol16 小时前
Superpowers GSD gstack AgentSkills深度测评
前端·人工智能
excel17 小时前
JavaScript 字符串与模板字面量:从表象到本质理解
前端