소스 검색

首页面板高度自适应 & 无缝滚动全屏模式修复

  1. 首页 panel-item 高度从固定 254px 改为 calc((100vh - 192px) / 3),按视口等比分配,大屏下保持设计稿比例
  2. SeamlessScroll 修复全屏切换后滚动停止的问题:
    - 添加 fullscreenchange 事件监听,全屏切换后重新初始化滚动
    - 存储 setTimeout 引用(_initTimer),initScroll 多次调用时清除旧定时器,防止多个 resume 同时触发
    - resume 开头先 pause,防止重复调用产生多个 rAF 动画循环
    - step 中 wrapper 为 null 时继续调度下一帧而非直接 return,避免动画链断裂
    - 滚动循环增加 maxScroll 兜底判断,容器变高导致 scrollTop 无法到达 resetHeight 时也能正常重置
画安 2 주 전
부모
커밋
309bfd754b
2개의 변경된 파일37개의 추가작업 그리고 10개의 파일을 삭제
  1. 32 9
      src/components/ui/SeamlessScroll.vue
  2. 5 1
      src/views/Home.vue

+ 32 - 9
src/components/ui/SeamlessScroll.vue

@@ -16,6 +16,7 @@ export default {
   data() {
     return {
       scrollTimer: null,
+      _initTimer: null,
       currentTop: 0,
       resetHeight: 0,
       isScrollable: false
@@ -44,6 +45,11 @@ export default {
   mounted() {
     console.log('✅ SeamlessScroll: 组件已挂载,准备初始化滚动');
     this.initScroll();
+    // 全屏切换后容器高度变化,需要重新初始化滚动
+    this._onFullscreenChange = () => {
+      setTimeout(() => this.initScroll(), 300);
+    };
+    document.addEventListener('fullscreenchange', this._onFullscreenChange);
   },
   watch: {
     data: {
@@ -56,10 +62,22 @@ export default {
   },
   beforeDestroy() {
     this.pause();
+    if (this._initTimer) {
+      clearTimeout(this._initTimer);
+      this._initTimer = null;
+    }
+    if (this._onFullscreenChange) {
+      document.removeEventListener('fullscreenchange', this._onFullscreenChange);
+    }
   },
   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;
 
@@ -71,14 +89,12 @@ export default {
 
       this.$nextTick(() => {
         // 加大一点延时,给表格充足的撑开时间
-        setTimeout(() => {
+        this._initTimer = setTimeout(() => {
+          this._initTimer = null;
           const wrapper = this.$refs.scrollRef;
           if (!wrapper) return;
-          
-          const measureEl = wrapper.querySelector(this.measureSelector);
 
-          // 【排查神器】:打印高度对比
-        //   console.log(`📏 尺寸核对 -> 容器可视高度: ${wrapper.clientHeight}px, 内容真实总高: ${wrapper.scrollHeight}px`);
+          const measureEl = wrapper.querySelector(this.measureSelector);
 
           // 如果容器高度等于或大于内容高度,说明没有溢出,肯定滚不动
           if (wrapper.scrollHeight <= wrapper.clientHeight) {
@@ -87,22 +103,29 @@ export default {
           }
 
           this.resetHeight = measureEl ? measureEl.offsetHeight / 2 : wrapper.scrollHeight / 2;
-        //   console.log('🚀 SeamlessScroll: 滚动初始化成功!复位锚点高度为:', this.resetHeight);
 
           this.resume();
-        }, 100); 
+        }, 100);
       });
     },
     resume() {
       if ((!this.data || this.data.length <= this.limit) || this.resetHeight <= 0) return;
+      // 防止重复调用产生多个动画循环
+      this.pause();
       const step = () => {
         const wrapper = this.$refs.scrollRef;
-        if (!wrapper) return;
+        if (!wrapper) {
+          // ref 暂时丢失(Vue 重渲染),不断链,等下一帧重试
+          this.scrollTimer = requestAnimationFrame(step);
+          return;
+        }
 
         this.currentTop += this.speed;
         wrapper.scrollTop = Math.floor(this.currentTop);
 
-        if (wrapper.scrollTop >= this.resetHeight) {
+        // 到达复位点,或者已滚到底部无法继续时,都重置
+        const maxScroll = wrapper.scrollHeight - wrapper.clientHeight;
+        if (wrapper.scrollTop >= this.resetHeight || (maxScroll > 0 && wrapper.scrollTop >= maxScroll)) {
           this.currentTop = 0;
           wrapper.scrollTop = 0;
         }

+ 5 - 1
src/views/Home.vue

@@ -292,10 +292,14 @@ export default {
   display: flex;
   flex-direction: column;
   gap: 16px;
+  height: 100%;
 }
 
 .panel-item {
-  height: 254px;
+  /* 可用高度 = 100vh - 80px(header) - 20px(上padding) - 60px(下padding)
+     3个面板 + 2个gap(16px) = 32px
+     每个面板 = (100vh - 192px) / 3 */
+  height: calc((100vh - 192px) / 3);
 }
 
 .table-panel ::v-deep .panel-content {