vue实现列表自动滚动(纯与原生方式)

11,492次阅读
没有评论

共计 10142 个字符,预计需要花费 26 分钟才能阅读完成。

Vue 实现列表自动滚动 (纯与原生方式)

 源码放在最后!

1. 效果展示:

 请添加图片描述

2. 功能说明:

该滚动可能存在的 Bug:

  • 1. 如果你写的大屏不是使用的接口轮询的方式可能会存在也页面空白的情况 (需要手动刷新才能触发列表滚动),因为我使用的是监听数据的变化然后做出响应,一般来说写大屏都会有个数据更新的机制。这一点需要你对源码进行改动。

3. 原理分析:

1. 核心数据和变量:
  • CityData:包含了所有的表格数据,是一个二维数组,每一行数据代表一个城市的信息。
  • CURRENTDATA:存储当前显示在屏幕上的行数据,动态更新以实现滚动效果。
  • COLUMNHEIGHT:存储每一行的高度,在滚动时通过调整前几行的高度来实现滑动效果。
  • CURRENTINDEX:表示当前显示的首行索引,在滚动过程中不断递增,最终通过循环回到起始行。
2. 数据初始化:

当组件挂载时,会通过 onMounted 钩子初始化表格数据。关键步骤包括:

  • 计算每列的宽度 HandelHeaderAverageWidthHandelColumnAverageWidth
  • 计算行的高度 HandelAverageHeight
  • 格式化数据并初始化 CityData,为每一行数据添加索引列,并根据 Alluserdata 动态填充内容。
3. 高度变化实现 (核心!)

滚动动画的关键在于每行的高度如何变化。为了模拟滚动,我们让行逐渐“消失”并再“出现”。具体实现步骤如下:

  • 初始行高度 :表格中的每一行都有固定的高度,在初始化时通过 HandelAverageHeight 计算每行的平均高度,并将结果存储在 COLUMNHEIGHT 中。

    const HandelAverageHeight = () => {
        const AverageHeight = height.value / columnNumber.value;
        AverageHeighT.value = AverageHeight;
        const TotalCount = 23;  
        COLUMNHEIGHT.value = new Array(TotalCount).fill(AverageHeight);
    };
    

    例如,假设每行的高度为 50px,这意味着表格中的所有行在初始时都是统一高度的。

  • 高度变化模拟滚动 :在开始动画时,StartAnimation 函数负责将某些行的高度设置为 0,模拟这些行滚动出可视区域。

    COLUMNHEIGHT.value.splice(0, MoveNumber, ...new Array(MoveNumber).fill(0));
    

    这个 splice 操作将 COLUMNHEIGHT 数组中前 MoveNumber 行的高度设置为 0,因此这些行会逐渐缩小到不可见,产生滚动的效果。

    当行的高度变为 0 时,浏览器会根据 CSS 的 transition 动画特性让该行逐渐收缩,这就是为什么行从视野中“滑动”出去,而不是瞬间消失。

4. 数据无限循环的过程 :

为了让滚动效果无限循环,需要对 CityData(存储所有表格数据)和 CURRENTDATA(存储当前显示的行数据)进行操作,使得数据能够循环显示。

  • 当前索引控制 :通过 CURRENTINDEX 控制当前显示的起始行。在每次动画开始时,会计算新的起始行索引,并根据这个索引来更新当前显示的数据。

    const alldataLenght = CityData.value.length;  
    const MoveNumber = 1;  
    const index = CURRENTINDEX.value;  
    

    CURRENTINDEX 会随着每次滚动递增,当它达到数据集的长度时,循环回到开头,确保表格数据可以无限滚动。

  • 数据循环拼接 :为了避免滚动到最后几行时的“断层”,StartAnimation 函数会将 CityData 从当前索引开始的数据提取出来,并将前面的数据拼接到后面,形成一个无缝连接的效果。

    const rowdata = cloneDeep(CityData.value);  
    const rows = rowdata.slice(index);  
    rows.push(...rowdata.slice(0, index));  
    

    比如当表格已经滚动到倒数第二行时,slice(index) 提取出剩下的最后几行,而 slice(0, index) 则从头部提取最开始的几行,并将它们拼接在一起。这使得滚动能够无缝连接。

    这样,在 CURRENTDATA 中,数据始终是连续的,模拟了数据无限循环滚动的效果。

5. 递归定时器控制滚动:

滚动效果通过递归调用 StartAnimation 来实现,每次滚动后等待一段时间,然后再执行下一次滚动。这就像一个“无尽的循环”,使表格持续滚动。

  • 延迟机制 :在每次滚动之前,会通过 await 等待一段时间,控制滚动的速度。

    await new Promise(resolve => setTimeout(resolve, awaitTime));  
    

    这里的 awaitTime 控制了滚动的速度,500ms 表示每次滚动间隔 0.5 秒。

  • 高度动画延迟 :滚动分为两个阶段,先将行的高度逐渐设置为 0,然后让下一行进入视野。这个过程通过两次 setTimeout 来控制。

    await new Promise(resolve => setTimeout(resolve, 800 - awaitTime));  
    

    这个延迟的机制让滚动变得平滑,防止表格中的数据瞬间跳跃。

4. 完整源码

首先你要有 pinia、loadsh、uuid 这些库

这是我的文件目录,有点不规范,你可以自己修改 hook 可以抽取一下!
vue 实现列表自动滚动 (纯与原生方式)

PlanList.vue


`;
} else {
columnIndex.value[i] = `

`;
}
// 生成数据行
for (let i = 0; i

`;

// 生成数据行
for (let j = 0; j ${text}`);
} else {
CityData.value[i].push(`

${text}

`);
}
}
}

}
Handeldata();
});

onMounted(() => {

StartAnimation()
Handeldata()
HandelHeaderAverageWidth()
HandelColumnAverageWidth()
HandelAverageHeight()
})

init.ts
import { ref, onMounted } from 'vue'

const init = (id: string) => {
    const width = refnumber>(0)
    const height = refnumber>(0)

    
    onMounted(() => {
        const container = document.getElementById(id) as HTMLElement | null
        if (container) {
            const domwidth = container.clientWidth
            const domheight = container.clientHeight
            
            width.value = domwidth
            height.value = domheight
        }
    })

    return {
        width,
        height
    }
}

export default init
PanList 文件夹下的 Index.vue





Store 文件夹下面的 index.ts
import { defineStore } from 'pinia'


interface UserData {
    order: string;  
    shop: string;   
    rider: string;  
    newShop: string; 
    avgOrder: string; 
    Rowindex?: number; 
}


export const useAllUserDataStore = defineStore('AllUserData', {
    state: (): { Alluserdata: UserData[] } => ({
        Alluserdata: [
            { "order": "北京 -17%", "shop": "北京 -3%", "rider": "北京 +20%", "newShop": "北京 +14%", "avgOrder": "北京 -16%" },
            { "order": "上海 -19%", "shop": "上海 +12%", "rider": "上海 -15%", "newShop": "上海 +15%", "avgOrder": "上海 -21%" },
            { "order": "广州 +20%", "shop": "广州 -5%", "rider": "广州 +17%", "newShop": "广州 -15%", "avgOrder": "广州 +10%" },
            { "order": "深圳 -4%", "shop": "深圳 -23%", "rider": "深圳 -5%", "newShop": "深圳 +15%", "avgOrder": "深圳 -4%" },
            { "order": "南京 +10%", "shop": "南京 -6%", "rider": "南京 -7%", "newShop": "南京 -5%", "avgOrder": "南京 -22%" },
            { "order": "杭州 -3%", "shop": "杭州 +20%", "rider": "杭州 +16%", "newShop": "杭州 -11%", "avgOrder": "杭州 -17%" },
            { "order": "合肥 +8%", "shop": "合肥 +17%", "rider": "合肥 -23%", "newShop": "合肥 +6%", "avgOrder": "合肥 -11%" },
            { "order": "济南 -13%", "shop": "济南 -11%", "rider": "济南 +6%", "newShop": "济南 -7%", "avgOrder": "济南 +18%" },
            { "order": "太原 -11%", "shop": "太原 -10%", "rider": "太原 +18%", "newShop": "太原 -14%", "avgOrder": "太原 -2%" },
            { "order": "成都 +19%", "shop": "成都 -6%", "rider": "成都 +14%", "newShop": "成都 -19%", "avgOrder": "成都 +10%" },
            { "order": "重庆 -12%", "shop": "重庆 +12%", "rider": "重庆 +12%", "newShop": "重庆 +7%", "avgOrder": "重庆 -3%" },
            { "order": "苏州 +15%", "shop": "苏州 +18%", "rider": "苏州 -8%", "newShop": "苏州 -3%", "avgOrder": "苏州 +4%" },
            { "order": "无锡 -15%", "shop": "无锡 -19%", "rider": "无锡 -14%", "newShop": "无锡 +21%", "avgOrder": "无锡 -21%" },
            { "order": "常州 +12%", "shop": "常州 +10%", "rider": "常州 +5%", "newShop": "常州 -22%", "avgOrder": "常州 -13%" },
            { "order": "温州 -14%", "shop": "温州 -16%", "rider": "温州 -15%", "newShop": "温州 +14%", "avgOrder": "温州 +17%" },
            { "order": "哈尔滨 +5%", "shop": "哈尔滨 -18%", "rider": "哈尔滨 -18%", "newShop": "哈尔滨 -18%", "avgOrder": "哈尔滨 -20%" },
            { "order": "长春 +14%", "shop": "长春 -5%", "rider": "长春 -17%", "newShop": "长春 +18%", "avgOrder": "长春 +4%" },
            { "order": "大连 -4%", "shop": "大连 -15%", "rider": "大连 -22%", "newShop": "大连 +14%", "avgOrder": "大连 +14%" },
            { "order": "沈阳 -1%", "shop": "沈阳 -21%", "rider": "沈阳 -15%", "newShop": "沈阳 +24%", "avgOrder": "沈阳 +8%" },
            { "order": "拉萨 +18%", "shop": "拉萨 +5%", "rider": "拉萨 -6%", "newShop": "拉萨 -24%", "avgOrder": "拉萨 -10%" },
            { "order": "呼和浩特 +14%", "shop": "呼和浩特 -12%", "rider": "呼和浩特 +8%", "newShop": "呼和浩特 +9%", "avgOrder": "呼和浩特 -21%" },
            { "order": "武汉 -21%", "shop": "武汉 +13%", "rider": "武汉 -10%", "newShop": "武汉 -14%", "avgOrder": "武汉 +10%" },
            { "order": "南宁 -22%", "shop": "南宁 +23%", "rider": "南宁 -9%", "newShop": "南宁 +6%", "avgOrder": "南宁 -12%" }
        ] 
    }),
    actions: {
        
        setAllUserData(newData: UserData[]) {
            
            this.Alluserdata = newData.map((item, index) => ({
                ...item,
                Rowindex: index + 1 
            }));
            console.log("Store updated");
        },
        
        addData(data: UserData) {
            const newRowIndex = this.Alluserdata.length ? Math.max(...this.Alluserdata.map(item => item.Rowindex!)) + 1 : 1;
            this.Alluserdata.push({ ...data, Rowindex: newRowIndex });
        },

        getAllUserData() {
            return this.Alluserdata;
        }
    }
});
App.vue





原文地址: vue 实现列表自动滚动 (纯与原生方式)

    正文完
     0
    Yojack
    版权声明:本篇文章由 Yojack 于2024-11-04发表,共计10142字。
    转载说明:
    1 本网站名称:优杰开发笔记
    2 本站永久网址:https://yojack.cn
    3 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
    4 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
    5 本站所有内容均可转载及分享, 但请注明出处
    6 我们始终尊重原创作者的版权,所有文章在发布时,均尽可能注明出处与作者。
    7 站长邮箱:laylwenl@gmail.com
    评论(没有评论)