n个容器元素实现无限滚动的实现代码
GPT4.0+Midjourney绘画+国内大模型 会员永久免费使用!
【 如果你想靠AI翻身,你先需要一个靠谱的工具! 】
场景
如何正确渲染多达10000个元素的列表。
无限下拉加载技术使用户在大量成块的内容面前一直滚动查看。这种方法是在你向下滚动的时候不断加载新内容。
当你使用滚动作为发现数据的主要方法时,它可能使你的用户在网页上停留更长时间并提升用户参与度。随着社交媒体的流行,大量的数据被用户消费。无线滚动提供了一个高效的方法让用户浏览海量信息,而不必等待页面的预加载。
如果我们换一种思维方式,如果一个页面上面有10000个通栏组件构成,我们如何使用5个组件动态去渲染整个页面呢?
解决思路
在对长列表的优化中我们有很多的方案,例如分页,懒加载等等。 有非常好的交互(菊花图),让用户去等待一下。这样的方案也是非常的成功。
如何另辟蹊径?
1. 在单页运用中我们是否可以对分页方案做一次分析,每页10个元素,分页每次渲染10个元素根据
2. 我们能否用一个支架撑起整个列表的长度 当屏幕滚动到对应的位置就渲染对应的10个元素
闪烁问题
当我们按这个思路实现后会出现闪烁的问题,由于滚动事件比较频繁,我们发现在我们看见的元素在不断的重新绘制,因为我们每次的过场是找到哪十个元素是需要渲染的,然后就直接替换了
这里的思路是我在看的到屏幕位置渲染10个再向上和向下各衍生渲染10个,总共30个,控制渲染的时候总是替换最上面或者最下面的元素,这样处于中间我们可以看见的部分就没有做重新重绘渲染。只是在预渲染的数据在做重绘。
原理
实现一个组件,可以显示具有5个元素的固定窗口大小的n个项目的列表: 即在任何时候,无限滚动n元素上也仅存在5个DOM容器。
1 2 3 4 | < ul > < li style = "transform: translate3d(0px, 0px, 0px);" >……</ li > < li style = "transform: translate3d(0px, 60px, 0px);" >……</ li > </ ul > |
- 长列表内容一般都有规律,这个列表的高度是可以直接通过<li>的个数直接算出来的,比如1000个元素,每个元素的高是60px,那么我们很快就能算出真个列表的高度是60000px
- 有了容器后将内部的<li>相对于<ul>进行绝对定位,在用js直接算出每一个<li> 对应的'transform: translate3d(0px, 0px, 0px);'属性
- 通过监听scroll时间不断的去找到当前位置需要渲染的<li>的, 和上一组渲染数据进行对比,有相同的<li>就跳过,找出和上一组渲染数据的不同元素,然后做对应的替换
例如: 100 个元素,首页就显示5个,初始化[0,1,2,3,4] 这5个<li>需要渲染,当我向下滚动一点的时候出现 [1,2,3,4,5] 这几个<li>需要渲染,这时候不要直接做整体替换,只应该替换差异项,结构应为[5,1,2,3,4],由于是绝对定位,会脱离标准流,单个的重绘不会影响其他四个,从而提升性能。
如何实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | <!DOCTYPE html> < html lang = "en" > < head > < meta charset = "UTF-8" > < meta name = "viewport" content = "width=device-width, initial-scale=1.0" > < meta http-equiv = "X-UA-Compatible" content = "ie=edge" > < script src = "https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js" ></ script > < title >Document</ title > < style > body, ul, li { margin: 0; padding: 0; list-style: none; } ul { position: relative; } ul li { position: absolute; top: 0; width: 100%; height: 31px; line-height: 32px; border-bottom: 1px solid #ccc; } </ style > </ head > < body > < ul > </ ul > </ body > < script > //总容器 var list = []; // 可视范围内元素容器 var showList = []; // 渲染容器 var renderList = []; // 每个容器的高度 var lineHeight = 32 // 初始化1000 个元素 for (var i = 0; i < 1000 ; i++) { list.push({ id: i, text: '第' + (i + 1) + '个元素', top: i * lineHeight, bottom: (i + 1) * lineHeight }) } // 初始化容器高度 $('ul').attr('style', 'height:' + 1000 * lineHeight + 'px') // 寻找初始化容器的 function render(top, bottom) { showList = [] // 标记哪些数据和已经渲染后的是重复的,这部分将不会重复渲染 var sameIndex = [] // 找出滚动位置再哪一个元素上 var currentIndex = 0 for (var i = 0 ; i < list.length; i++) { var item = list [i] if (item.top <= window.scrollY && item.bottom > window.scrollY) { currentIndex = i; break; } } var range = 0 // 在当前找到的元素分别向上和向下找需要显示的元素,总共的个数达到35个为止 while (range < 100 && showList.length + sameIndex.length < 35) { if (currentIndex - range >= 0) { // 对比满足条件的元素是否在已经渲染列表中,在就做标记,不在就放在showList 当时候替换没有被标记的元素 if (renderList.includes(list[currentIndex - range].id)) { // sameIndex.push(currentIndex-range) sameIndex.unshift(renderList.indexOf(list[currentIndex - range].id)) } else { showList.unshift(list[currentIndex - range]) } } if (currentIndex + range < list.length && range != 0) { if (renderList.includes(list[currentIndex + range].id)) { sameIndex.push(renderList.indexOf(list[currentIndex + range].id)) } else { showList.push(list[currentIndex + range]) } } range++ } // 将对比出来的新的需要渲染的元素和没有被标记的渲染列表元素做替换 if (renderList.length > 0) { for (var i = 0; i < renderList.length ; i++) { if (!sameIndex.includes(i) && showList.length) { renderList[i] = showList.shift().id $('ul li').eq(i).html(list[renderList[i]].id + list[renderList[i]].text).attr('style', 'transform: translate3d(0px, ' + list[renderList[i]].top + 'px, 0px);') } } } else { // 第一次进行初始化列表 renderList = showList .map(function (val) { return val.id }) renderList.map(function (key) { $('ul').append($('<li style = "transform: translate3d(0px, ' + list[key].top + 'px, 0px);" ">#' + list[key].id + list[key].text + '</ li >')) }) } console.log(renderList) } // 第一次渲染 render() $(window).scroll(function (e) { render() }); </ script > </ html > |
TODO
- 对比替换容器元素的方法总是感觉还能优化,这样就能提升运行效率,从而优化快速滚动出现的白屏
- 这里也出一个思考题[0,1……,10000], 每次从中取出5个元素,新选出来的和旧的进行对比,保留两个数组的交集部分,用新数组里面的新元素替换老数组里面的非交集部分, 例如第一次是[0,1,2,3,4],第二次是[2,3,4,5,6] 那么对比后生成[5,6,2,3,4],第三次如果是[4,5,6,7,8],生成的就为[5,6,7,8,4]。用最少的代码数得到这个结果数组。
小结
- 通过数据完成布局和初始化
- 通过觉得定位让容器脱离标准流
- 通过数据对比,找出差异容器元素,每次重绘尽量少的容器元素。
下期-----组件搭配实现长列表
关键点
- 组件不同于有规律的list, 组件的高度有可能不可控,会根据不同的手机设备而出现不用的高度
- 由于组件高度的同,渲染区域内有几个组件的数量也是不一样的,这就造成了容器个数不一样
- 对于高度不好通过数据直接进行计算的,如果需要一次整体渲染然后通过dom去计算位置和高度是非常耗费第一次加载性能的。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
相关文章
- 在 Web 开发中,文本的视觉效果是提升用户体验的重要因素之一,通过 CSS 技巧,我们可以创造出许多独特的效果,例如文字镂空效果,本文将带你一步一步实现一个简单的文字镂空2024-11-17
- 在Html中,a标签默认的超链接样式是蓝色字体配下划线,这可能不满足所有设计需求,如需去除这些默认样式,可以通过CSS来实现,本文给大家介绍Html去除a标签的默认样式的操作代码2024-09-25
- 在HTML中,可以通过设置CSS的resize属性为none,来禁止用户手动拖动文本域(textarea)的大小,这种方法简单有效,适用于大多数现代浏览器,但需要在老旧浏览器中进行测试以确保2024-09-25
- 本文详细介绍了如何利用HTML和CSS实现多种风格的进度条,包括基础的水平进度条、环形进度条以及球形进度条等,还探讨了如何通过动画增强视觉效果,内容涵盖了使用HTML原生标签2024-09-19
- Canvas 提供了一套强大的 2D 绘图 API,适用于各种图形绘制、图像处理和动画制作,可以帮助你创建复杂且高效的网页图形应用,这篇文章主要介绍了HTML中Canvas关键知识点总结2024-06-03
- 本文主要介绍了html table+css实现可编辑表格的示例代码,主要使用HTML5的contenteditable属性,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习2024-03-06
- 本文主要介绍了HTML中使用Flex布局实现双行夹批效果,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习2024-02-22
- 在网站开发中,登录页面是必不可少的一部分,本文就来介绍一下HTML+CSS实现登录切换,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需2024-02-02
- 本文主要介绍了HTML+CSS实现全景轮播的示例代码,实现了一个简单的网页布局,其中包含了五个不同的盒子,每个盒子都有一个不同的背景图片,并且它们之间有一些间距,下面就2024-02-02
- 来到圣诞节了,那么就可以制作一颗HTML的圣诞树送给朋友,没有编程基础的小白也可以按照步骤操作也可以运行起来代码的,喜欢的朋友快来体验吧2023-12-26
最新评论