| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 |
- <template>
- <div class="seamless-scroll-container" ref="scrollRef" @mouseenter="pause" @mouseleave="resume">
- <slot :list="scrollData"></slot>
- </div>
- </template>
- <script>
- export default {
- name: 'SeamlessScroll',
- props: {
- data: { type: Array, required: true },
- limit: { type: Number, default: 4 },
- speed: { type: Number, default: 0.5 },
- measureSelector: { type: String, default: 'tbody' }
- },
- data() {
- return {
- scrollTimer: null,
- currentTop: 0,
- resetHeight: 0,
- isScrollable: false
- }
- },
- computed: {
- scrollData() {
- if (this.data && this.data.length > this.limit) {
- this.isScrollable = true;
- const clone = JSON.parse(JSON.stringify(this.data)).map((item, index) => ({
- ...item,
- _clone_id: `clone_${Date.now()}_${index}`
- }));
- return [...this.data, ...clone];
- }
- this.isScrollable = false;
- return this.data;
- }
- },
- // 加入 mounted 钩子,确保 DOM 绝对渲染完毕再测算
- mounted() {
- console.log('✅ SeamlessScroll: 组件已挂载,准备初始化滚动');
- this.initScroll();
- },
- watch: {
- data: {
- handler() {
- console.log('🔄 SeamlessScroll: 监测到数据变化');
- this.initScroll();
- },
- deep: true
- }
- },
- beforeDestroy() {
- this.pause();
- },
- methods: {
- initScroll() {
- this.pause();
- this.currentTop = 0;
- if (this.$refs.scrollRef) this.$refs.scrollRef.scrollTop = 0;
- if (!this.isScrollable) {
- console.log('🛑 SeamlessScroll: 数据量不足,无需滚动');
- return;
- }
- this.$nextTick(() => {
- // 加大一点延时,给表格充足的撑开时间
- setTimeout(() => {
- const wrapper = this.$refs.scrollRef;
- if (!wrapper) return;
-
- const measureEl = wrapper.querySelector(this.measureSelector);
- // 【排查神器】:打印高度对比
- // console.log(`📏 尺寸核对 -> 容器可视高度: ${wrapper.clientHeight}px, 内容真实总高: ${wrapper.scrollHeight}px`);
- // 如果容器高度等于或大于内容高度,说明没有溢出,肯定滚不动
- if (wrapper.scrollHeight <= wrapper.clientHeight) {
- console.warn('⚠️ SeamlessScroll 警告: 内容高度没有超出容器高度,滚动被迫终止!请检查外部 CSS 高度限制。');
- return;
- }
- this.resetHeight = measureEl ? measureEl.offsetHeight / 2 : wrapper.scrollHeight / 2;
- // console.log('🚀 SeamlessScroll: 滚动初始化成功!复位锚点高度为:', this.resetHeight);
- this.resume();
- }, 100);
- });
- },
- resume() {
- if (!this.isScrollable || this.resetHeight <= 0) return;
- const step = () => {
- const wrapper = this.$refs.scrollRef;
- if (!wrapper) return;
- this.currentTop += this.speed;
- wrapper.scrollTop = Math.floor(this.currentTop);
- if (wrapper.scrollTop >= this.resetHeight) {
- this.currentTop = 0;
- wrapper.scrollTop = 0;
- }
- this.scrollTimer = requestAnimationFrame(step);
- };
- this.scrollTimer = requestAnimationFrame(step);
- },
- pause() {
- if (this.scrollTimer) {
- cancelAnimationFrame(this.scrollTimer);
- this.scrollTimer = null;
- }
- }
- }
- }
- </script>
- <style scoped>
- .seamless-scroll-container {
- width: 100%;
- height: 100%;
- overflow: hidden;
- scrollbar-width: none;
- -ms-overflow-style: none;
- }
- .seamless-scroll-container::-webkit-scrollbar {
- display: none;
- }
- </style>
|