不定高度虚拟滚动实现方案
作者:秋了秋 发表时间:2023年12月09日
Web前端界的虚拟滚动方案都是假设所有行高一样,通过计算屏幕可视高度计算出科容纳有多少行数据从而插入多少行dom,至于滚动条是计算所有行需要的高度然后用padding撑起来。这种虚拟滚动有一个缺陷就是必须是单行,或者每行数据高度一致,不允许任意换行,如何解决这个痛点?以下是我研究的实现方案,可参考。
- <div style="height:400px;width:800px;overflow: auto;border: 1px solid #ccc" id="scroller"><div id="container" ></div></div>
- <script type="text/javascript">
- // 模拟高度不等的一万条数据
- let dataArray = [], beginIndex = 0, segIndex = 0, beginSetHeight, lastBeginIndex, lastType;
- for(let i = 0; i < 10000; i++) {
- dataArray.push(i + '. ' + new Array(Math.ceil(Math.random() * 10)).fill('数据数据数据数据数据数据数据数据'));
- }
- let minCount = parseInt(parseInt(scroller.style.height) / 16); // 假设最小行高为16像素(一行字体的高度)
- // 初始化往容器填充维持滚动的最小量数据
- addContent('append');
- const scrollHeight = parseInt(scroller.style.height);
- let scrollTimer;
- scroller.onscroll = (e)=>{
- if(scrollTimer || beginSetHeight) {
- return; //beginSetHeight 禁止因设置高度触发onscroll
- }
- scrollTimer = setTimeout(function() {
- // 视窗高度
- var containerHeight = parseFloat(container.offsetHeight);
- // 滚动条位置
- var scrollTop = scroller.scrollTop;
- if (scrollTop + scrollHeight >= containerHeight) {
- console.log('滚动到达底部');
- addContent('append');
- } else if(scrollTop <= 0) {
- console.log('滚动到达顶部');
- addContent('prepend');
- }
- clearTimeout(scrollTimer);
- scrollTimer = null;
- }, 100)
- }
- function solveIndex() {
- // index临界处理
- if(beginIndex <= 0) {
- beginIndex = 0;
- }
- if(beginIndex + minCount >= dataArray.length - 1) {
- beginIndex = dataArray.length - 1 - minCount;
- }
- }
- function addContent(type) {
- const seg = document.createElement('div');
- seg.className = 'seg seg' + segIndex++;
- if(lastBeginIndex === beginIndex && lastType === type) {
- return;
- }
- beginSetHeight = true;
- // lastType !== type 反向滚动
- if(lastType !== type && lastType) {
- if(type === 'append') {
- beginIndex += minCount * 3;
- } else {
- beginIndex -= minCount * 3;
- }
- solveIndex();
- }
- for (let i = beginIndex; i < beginIndex + minCount; i++) {
- const p = document.createElement('p');
- p.innerHTML = dataArray[i];
- seg.appendChild(p);
- }
- if(type === 'append') {
- container.appendChild(seg);
- } else {
- container.insertBefore(seg, document.querySelector('.seg'));
- scroller.scrollTop = seg.offsetHeight;
- }
- lastBeginIndex = beginIndex;
- lastType = type;
- // 清理上上一个seg,页面始终只保留两个有内容的seg
- const segs = container.querySelectorAll('.seg');
- if(segs.length > 2) {
- const removeSeg = type === 'append' ? segs[0] : segs[segs.length - 1];
- const originScrollTop = scroller.scrollTop, height = removeSeg.offsetHeight;
- container.removeChild(removeSeg);
- scroller.scrollTop = type === 'append' ? originScrollTop - height : originScrollTop;
- }
- if(type === 'append') {
- beginIndex += minCount;
- } else {
- beginIndex -= minCount;
- }
- solveIndex();
- setTimeout(function() {
- beginSetHeight = false; //禁止因设置高度和代码设置scrollTop触发onscroll
- }, 100)
- }
- </script>
直接复制代码放到html页面里面的body标签内即可看到效果
0. 数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据
1. 数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据
2. 数据数据数据数据数据数据数据数据
3. 数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据
4. 数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据
5. 数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据
6. 数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据
7. 数据数据数据数据数据数据数据数据
8. 数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据
9. 数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据
10. 数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据
11. 数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据
12. 数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据
13. 数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据
14. 数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据
15. 数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据
16. 数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据
17. 数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据
18. 数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据
19. 数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据
20. 数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据
21. 数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据
22. 数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据
23. 数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据
24. 数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据,数据数据数据数据数据数据数据数据