Parcourir la source

修复SeamlessScroll滚动组件在全屏模式下不滚动的问题

画安 il y a 2 semaines
Parent
commit
33661cf77e
2 fichiers modifiés avec 36 ajouts et 17 suppressions
  1. 35 16
      src/components/ui/SeamlessScroll.vue
  2. 1 1
      src/views/Home.vue

+ 35 - 16
src/components/ui/SeamlessScroll.vue

@@ -24,27 +24,36 @@ export default {
   },
   computed: {
     scrollData() {
+      // 当数据量大于 limit 时,开启滚动
       if (this.data && this.data.length > this.limit) {
         this.isScrollable = true;
+        
         const original = this.data.map((item, index) => ({
           ...item,
           _originalIndex: index
         }));
-        const clone = JSON.parse(JSON.stringify(this.data)).map((item, index) => ({
-          ...item,
-          _clone_id: `clone_${Date.now()}_${index}`,
-          _originalIndex: index
-        }));
-        return [...original, ...clone];
+
+        // 【大屏终极修复】强制克隆多份(总共 4 份),保证内容高度绝对碾压任何全屏容器
+        let result = [...original];
+        for (let i = 0; i < 3; i++) {
+          const clone = JSON.parse(JSON.stringify(this.data)).map((item, index) => ({
+            ...item,
+            _clone_id: `clone_${Date.now()}_${i}_${index}`,
+            _originalIndex: index
+          }));
+          result = result.concat(clone);
+        }
+        return result;
       }
+      
       this.isScrollable = false;
       return this.data.map((item, index) => ({ ...item, _originalIndex: index }));
     }
   },
-  // 加入 mounted 钩子,确保 DOM 绝对渲染完毕再测算
   mounted() {
     console.log('✅ SeamlessScroll: 组件已挂载,准备初始化滚动');
     this.initScroll();
+    
     // 全屏切换后容器高度变化,需要重新初始化滚动
     this._onFullscreenChange = () => {
       setTimeout(() => this.initScroll(), 300);
@@ -53,11 +62,12 @@ export default {
   },
   watch: {
     data: {
-      handler() { 
+      handler() {
         console.log('🔄 SeamlessScroll: 监测到数据变化');
-        this.initScroll(); 
+        this.initScroll();
       },
-      deep: true
+      deep: true,
+      immediate: true
     }
   },
   beforeDestroy() {
@@ -73,17 +83,19 @@ export default {
   methods: {
     initScroll() {
       this.pause();
+      
       // 清除上一次未执行完的延时器,防止多个 setTimeout 同时触发 resume
       if (this._initTimer) {
         clearTimeout(this._initTimer);
         this._initTimer = null;
       }
+      
       this.currentTop = 0;
       if (this.$refs.scrollRef) this.$refs.scrollRef.scrollTop = 0;
 
       // 直接判断数据量,不依赖 computed 副作用的时序
       if (!this.data || this.data.length <= this.limit) {
-        console.log('🛑 SeamlessScroll: 数据量不足,无需滚动');
+        console.log('🛑 SeamlessScroll: 数据量不足,无需滚动', this.data, this.limit);
         return;
       }
 
@@ -94,15 +106,16 @@ export default {
           const wrapper = this.$refs.scrollRef;
           if (!wrapper) return;
 
-          const measureEl = wrapper.querySelector(this.measureSelector);
-
-          // 如果容器高度等于或大于内容高度,说明没有溢出,肯定滚不动
+          // 如果 4 份数据加起来都没容器高,说明数据极其短,强制取消滚动避免报错
           if (wrapper.scrollHeight <= wrapper.clientHeight) {
-            console.warn('⚠️ SeamlessScroll 警告: 内容高度没有超出容器高度,滚动被迫终止!请检查外部 CSS 高度限制。');
+            console.warn('⚠️ SeamlessScroll: 数据总高度依然小于容器,取消滚动。');
             return;
           }
 
-          this.resetHeight = measureEl ? measureEl.offsetHeight / 2 : wrapper.scrollHeight / 2;
+          const measureEl = wrapper.querySelector(this.measureSelector);
+
+          // 【核心修复】因为 computed 里总共渲染了 4 份数据,所以总高度要除以 4 才能得到单份的真实复位高度
+          this.resetHeight = measureEl ? measureEl.offsetHeight / 4 : wrapper.scrollHeight / 4;
 
           this.resume();
         }, 100);
@@ -110,8 +123,10 @@ export default {
     },
     resume() {
       if ((!this.data || this.data.length <= this.limit) || this.resetHeight <= 0) return;
+      
       // 防止重复调用产生多个动画循环
       this.pause();
+      
       const step = () => {
         const wrapper = this.$refs.scrollRef;
         if (!wrapper) {
@@ -125,12 +140,16 @@ export default {
 
         // 到达复位点,或者已滚到底部无法继续时,都重置
         const maxScroll = wrapper.scrollHeight - wrapper.clientHeight;
+        
+        // 只要卷去的高度达到了单份数据的真实高度,就瞬间复位,实现无缝循环
         if (wrapper.scrollTop >= this.resetHeight || (maxScroll > 0 && wrapper.scrollTop >= maxScroll)) {
           this.currentTop = 0;
           wrapper.scrollTop = 0;
         }
+        
         this.scrollTimer = requestAnimationFrame(step);
       };
+      
       this.scrollTimer = requestAnimationFrame(step);
     },
     pause() {

+ 1 - 1
src/views/Home.vue

@@ -89,7 +89,7 @@
         </div>
         <div class="panel-item">
           <PanelContainer title="关键路口" class="table-panel">
-            <SeamlessScroll :data="keyIntersectionData" :limit="4">
+            <SeamlessScroll :data="keyIntersectionData" :limit="2">
               <template #default="{ list }">
                 <TechTable :columns="keyIntersectionColumns" :data="list"
                   @row-click="onIntersectionRowClick" />