当前位置:首页 »“秋了秋”个人博客 » 前端编程 » 不定高度虚拟滚动实现方案

不定高度虚拟滚动实现方案

作者:秋了秋 发表时间: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
目录: 前端编程标签: 虚拟滚动 437次阅读

请求播放音乐,请点击播放

登 录
点击获取验证码
还没账号?点击这里