# virtual-list-scroll **Repository Path**: strivelei/virtual-list-scroll ## Basic Information - **Project Name**: virtual-list-scroll - **Description**: vue3 虚拟滚动列表,可以展示大量列表 - **Primary Language**: JavaScript - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 2 - **Created**: 2023-12-04 - **Last Updated**: 2026-04-02 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # virtual-seamless-scrolling ## 介绍 基于 **Vue 3** 的**虚拟列表 + 无缝自动滚动**组件:在只渲染可视区域附近若干行的前提下,通过 **`transform`** 位移与 **GSAP** 动画实现连续滚动;适合公告、榜单、大屏等需要循环展示大量数据的场景。 > 说明:当前仓库实现为 **Vue 3** 版本。若需 Vue 2,请自行拉取源码改造。 --- ## 技术栈与依赖 | 项 | 说明 | |----|------| | Vue | 3.x(组合式 API) | | 动画 | [GSAP](https://greensock.com/gsap/) 3.x,用于行级位移动画与行间停顿计时 | | 工具 | [@vueuse/core](https://vueuse.org/)(尺寸监听、元素/文档可见性等) | 发布包中会**打包**上述运行时依赖;业务项目只需安装本包与 **Vue 3**。 **本地开发 / 构建**建议使用 **Node.js `^20.19.0` 或 `>=22.12.0`**(与 Vite 8 要求一致)。 --- ## 安装 ```bash npm install virtual-seamless-scrolling -S ``` ```bash pnpm add virtual-seamless-scrolling ``` 也可从 Gitee 安装: ```bash npm install git+https://gitee.com/strivelei/virtual-list-scroll.git -S ``` ### 引入样式 组件样式需单独引入(若未引入,列表容器可能无高度或布局异常): ```ts import 'virtual-seamless-scrolling/dist/style.css'; ``` ### 按需引入组件 ```ts import { VirtualListScroll, ListHeader } from 'virtual-seamless-scrolling'; import type { TitltListItem } from 'virtual-seamless-scrolling'; ``` --- ## 架构与原理(简要) 1. **虚拟窗口**:根据容器高度与首行测量高度估算 `maxCount`,只渲染 `startIndex ~ endIndex` 区间的数据行。 2. **无缝带**:在可视列表下方再渲染一段「头部数据」副本,视觉上形成循环;滚动到底后内部会将位移重置并触发 `scroll-end`,由业务决定是否追加数据等。 3. **位移方式**:列表在 **`overflow: hidden`** 容器内通过外层 **`translate3d`** 移动,避免高频修改 `scrollTop`;**鼠标滚轮**通过监听 `wheel` 手动累加内部位移,与自动滚动共用同一套 `scrollPixel` 逻辑。 4. **自动滚动**:使用 **GSAP** 的 `gsap.to` 对位移做插值;**`transitionDuration`** 控制「滚过一行高度」所需时间;**`interval`** 控制「每滚完一段后的静止等待」时间(详见下文)。 --- ## VirtualListScroll 组件 ### Props | 属性 | 说明 | 类型 | 默认值 | 必填 | |------|------|------|--------|------| | `dataKey` | 数据源对象上用作 `v-for` `:key` 的字段名,需在每条数据中唯一 | `string` | `'id'` | 是(建议始终传入业务主键) | | `dataSource` | 列表数据数组,每项为对象且包含 `dataKey` 对应字段 | `Array` | — | 是 | | `loading` | 加载中。为 **`true` 时停止自动滚动**;为 **`false` 且未悬停**时才会自动滚 | `boolean` | — | 是 | | `interval` | **行间停顿**时长(**毫秒**)。为 `0` 时连续滚动、**不触发** `line-scroll-end`;大于 `0` 时,每滚完一段后静止 `interval` 再滚下一段,并在满足条件时触发 `line-scroll-end` | `number` | `0` | 否 | | `transitionDuration` | **单行过渡动画**基准时长(**毫秒**)。表示滚过「**一整行行高**」所用的动画时间;若本段实际位移不足一行(例如滚轮停在半行后先「补到行首」),动画时长会按距离比例缩短,以保持线速度一致 | `number` | `1000` | 否 | | `refresh` | `true`:数据源变化时**清空内部累积列表**再按新数据渲染;`false`:在原有基础上**追加**新条目(适合分页追加) | `boolean` | `false` | 否 | #### `interval` 与 `transitionDuration` 的区别 - **`transitionDuration`**:内容**在动**的时间。内部对应 GSAP 对 `scrollPixel` 的补间时长,按 `(实际像素位移 / 行高) × transitionDuration` 计算。 - **`interval`**:内容**停住不动**的时间。在 `interval > 0` 时,每段滚动结束后会再执行一次「仅占位、不位移」的 `gsap.to`,时长为 `interval`,用于间隔下一行滚动。 二者**数值可以不同**,分别控制「动多快」与「停多久」。 #### `interval > 0` 时的额外行为 - **首次**开始自动滚动前,会先**静止等待一个完整的 `interval`**,再开始第一段位移动画(与「每行滚完再停」的节奏一致)。 - 将 `refresh` 设为 `true` 并清空/替换数据时,会重置「首段等待」状态,新数据加载后仍会先等 `interval` 再滚。 ### 事件 | 事件名 | 说明 | 触发时机 | |--------|------|----------| | `scroll-end` | 列表在内部逻辑中判定「滚到本轮末尾」并回到起点时触发 | 虚拟窗口滑出数据末尾、`scrollPixel` 被重置为 `0` 时;可用于请求下一页、拼接数据等 | | `line-scroll-end` | **单行(整行高)滚动完成** | 仅当 **`interval > 0`**,且本段动画位移**约等于一行高**时触发;**补行首的短距离**段**不会**触发,避免与下一整行连续触发两次 | 在模板中使用 **kebab-case**: ```vue ``` ### 插槽 | 插槽名 | 作用域参数 | 说明 | |--------|------------|------| | `item` | `{ item }` | 渲染**一行**内容;`item` 为 `dataSource` 中对应数据对象 | ### 交互行为 | 行为 | 说明 | |------|------| | 鼠标进入列表区域 | **暂停**自动滚动 | | 鼠标离开 | **`loading` 为 false** 时**恢复**自动滚动 | | 鼠标滚轮 | 在列表上滚动时**阻止默认冒泡**,改为更新内部位移;非悬停且非 loading 时会**打断当前 GSAP 动画并重新排队**自动滚动链 | | 元素离开视口 / 页签隐藏 | 会按内部逻辑暂停;再次可见时尝试恢复(与 `mouseenter` 状态叠加时以组件内判断为准) | ### 样式与布局建议 - 组件根节点使用 **`height: 100%`**,请保证**父级有明确高度**(如固定 `height` 或 flex 子项拉伸),否则可视高度为 0 会导致无法正确测量行高。 - 列表项建议 **`box-sizing: border-box`**,且同一列表内各行**高度一致**时测量最准确(当前实现按首行高度估算虚拟行高)。 --- ## ListHeader(可选) 与示例配套的表头组件,通过 `titleList` 配置列标题与宽度,类型为 `TitltListItem[]`。具体字段见源码 `src/components/ListHeader/data.d.ts`。 --- ## 完整示例 更完整的交互可参考仓库内 **`src/examples/App.vue`**。 ```vue ``` --- ## 本地开发 ```bash pnpm install pnpm dev ``` ## 构建库 ```bash pnpm run build ``` 产物在 **`dist/`**(含 ES / UMD 与 `style.css`)。 --- ## 常见问题(FAQ) **Q:`loading` 已经 `false` 了,为什么不滚?** A:请确认已有数据且容器已布局完成(行高能测到)。组件会在量完行高后排队启动;若仍异常,检查父级高度是否为 0。 **Q:`line-scroll-end` 为什么不触发?** A:需要 **`interval > 0`**,且本段为**约一整行高**的滚动;`interval === 0` 时不会发该事件。 **Q:`transitionDuration` 和 `interval` 能写成一样吗?** A:可以,但语义不同:前者是**动画时长**,后者是**停顿时长**;相同数值表示「动多久、就停多久」的节奏感。 --- ## 反馈与贡献 有其他需求或 Bug,欢迎在 **Gitee** 提 Issue,后续会持续迭代优化。