# 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,后续会持续迭代优化。