不定高度虚拟滚动实现方案
作者:秋了秋 发表时间: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
文章作者: “秋了秋”个人博客,本站鼓励原创。
转载请注明本文地址:http://netblog.cn/blog/506.html