Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

手动实现虚拟滚动列表 #116

Open
deepthan opened this issue Nov 26, 2020 · 0 comments
Open

手动实现虚拟滚动列表 #116

deepthan opened this issue Nov 26, 2020 · 0 comments

Comments

@deepthan
Copy link
Owner

deepthan commented Nov 26, 2020

手动实现虚拟滚动列表

什么是虚拟滚动列表?

根据滚动容器元素的可视区域来渲染长列表数据中某一部分数据的技术。

即: 仅渲染可视区域渲染的列表。

  • 滚动容器元素:一般情况下,滚动元素是window对象,然而,我们可以通过布局的方式,在某个页面中任意指定一个或多个滚动容器元素。
  • 可滚动区域:滚动容器的内部内容区域。若有10条数据,每个列表项的高度是50, 那么可滚动区域的高度就是 10*50, 可滚动区域当前的具体高度值一般可以通过滚动容器的scrollHeight属性获取,用户可以通过滚动来改变列表在可视区域的显示部分。
  • 可视区域:滚动容器的视觉可见区域。如果滚动元素是window对象,可视区域就是浏览器的视口大小;如果容器元素是某个div元素,其高度是300,右侧有纵向滚动条可以滚动,那么视觉可见区域就是可视区域。

为什么用它?

虚拟列表是对长列表的一种优化方案。

长列表:

  • 概念: 在前端开发中,会碰到一些不能使用分页方式来加载列表数据的业务形态,我们称这种列表叫做长列表。比如,在一些外汇交易系统中,前端会准实时的展示用户的持仓情况(收益、亏损、手数等),此时对于用户的持仓列表一般是不能分页的。
  • 优劣: 可以一次性把所有数据实时展现出来,但是渲染时间较长,数据量大会导致页面卡顿。

非完整的长列表渲染一般有两种方式

  • 按需渲染,如虚拟列表
  • 延迟渲染,如无限滚动

如何实现?

原理介绍

实现虚拟列表就是在处理用户滚动时,要改变列表在可视区域的渲染部分:

  • 计算当前可见区域起始数据的 startIndex
  • 计算当前可见区域结束数据的 endIndex
  • 计算当前可见区域的数据,并渲染到页面
  • 计算 startIndex 对应的数据在整个列表中的偏移位置 startOffset,并设置到列表上
  • 计算 endIndx 对应的数据相对于可滚动区域最底部的偏移位置 endOffset, 并设置到列表上。

image

https://note.youdao.com/yws/public/resource/8d791dfc6f9c6a22e8e6e722cf68f002/xmlnote/8809167AEAD242EA8C1A1C809C31DC92/36325

从上可以看出, startOffestendOffset会撑开容器元素的内容高度,让其可持续的滚动;此外,还能保持滚动条处于一个正确的位置。

原理

根据数据的总条数(total)和每条数据的高度(itemHeight)进行计算

  • 假设可视区域可以放10条数据
  • 当前滚动到可看到的第一条数据的下标是 50,那么最后一条的下标则是59
  • 那么上面的padding是 40*itemHeight
  • 下面的padding是 (total-59)*itemHeight
  • 这时候记录每个显示出来元素的相关信息,如元素顶部离容器的距离:topOffset = offsetTop,元素底部离容器的距离:bottomOffset = offsetTop + 元素的高度,元素的index,

每次滚动的时候

  • 获得此时的 scrollTop,即此次滚动的卷曲值,从缓存的元素信息中查找到第一个bottomOffset值大于scrollTop的元素,并以此元素的index为开始,index + 可视数据数量为结束,从总的数据中截取
  • 还需要将此时的元素的topOffset作为上半部门的padding值,这样才会有滚动效果。

image
https://note.youdao.com/yws/public/resource/8d791dfc6f9c6a22e8e6e722cf68f002/xmlnote/237BA06A0DD94538A1A8F0A293E59305/36393

image
https://note.youdao.com/yws/public/resource/8d791dfc6f9c6a22e8e6e722cf68f002/xmlnote/4F260F6B398B46B8A80119F2A5878442/36395

ref

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant