当前位置: 首页 > news >正文

武安网站设计公司沈阳seo按天计费

武安网站设计公司,沈阳seo按天计费,php微信微网站怎么做,自己做一款商城app用原生JS实现虚拟列表 介绍 最近在开发需求的时候,有用到 Antd 的虚拟列表组件 rc-virtual-list ,粗略地看了一下源码,于是萌生了自己写一个虚拟列表的想法。当一个列表需要渲染大量数据的时候是非常耗时的,而且在列表滚动的过程…

用原生JS实现虚拟列表

介绍

  • 最近在开发需求的时候,有用到 Antd 的虚拟列表组件 rc-virtual-list ,粗略地看了一下源码,于是萌生了自己写一个虚拟列表的想法。
  • 当一个列表需要渲染大量数据的时候是非常耗时的,而且在列表滚动的过程中会出现卡顿的现象。即使用上懒加载解决了列表初始化时渲染过慢的问题,但是每次拉取下一页数据的时候都会造成列表的重新渲染。随着拉取的数据越来越多,列表渲染时间长、卡顿的问题依然存在。
  • 这个时候虚拟列表就派上用场了。虚拟列表的实现原理简单来说,就是列表并不会把所有的数据都渲染出来,而是通过监听滚动事件然后实时计算当前是哪几条数据显示在页面上,然后只渲染用户可以看见的这几条数据。
  • 在网上找了张图可以说明的更生动些:
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vbz6hBM5-1684195418798)(C:\Users\quyanliang\AppData\Roaming\Typora\typora-user-images\1684195219996.png)]

定高虚拟列表

  • 实现虚拟列表需要两层的div:

  • <style>.list-container {overflow: auto;border: 1px solid black;height: 500px;}
    </style><body><!-- 外部容器用来固定列表容器的高度,同时生成滚动条 --><div class="list-container"><!-- 内部容器用来装元素,高度是所有元素高度的和 --><div class="list-container-inner"></div></div>Ï
    </body>
    
  • 外层的div用来固定高度,也就是图上展示的B区域。内层的div用来装列表元素,也就是图上展示的A区域。给外层的div设置 overflow: auto ,这样当列表元素数量超过B区域的高度时就会出现滚动条。

  • 接下来,我们需要监听外层div的滚动事件,实时计算出显示在可视区域上的第一个元素的坐标 startIndex 和 最后一个元素的坐标 endIndex。假设列表每个元素的高度都是60px,那么 startIndex 就等于外层 div 顶部卷起来的长度除以列表元素的高度,而 endIndex 就等于 startIndex + 可视区域能展示的元素个数 :

  • const itemHeight = 60
    const height = 500
    const startIndex = Math.floor(outerContainer.scrollTop / itemHeight)
    const endIndex = startIndex + Math.ceil(height / itemHeight)
    
  • 然后用 startIndex 和 endIndex 去截取数据,并渲染在内层 div 上:

  • const viewData = data.slice(startIndex, endIndex + 1)
    const innerContainer = document.querySelector('.list-container-inner')
    innerContainer.innerHTML = ''
    for(let i = 0; i < viewData.length; i++) {const item = document.createElement('div')item.innerHTML = viewData[i]innerContainer.appendChild(item)
    }
    
  • 最后一步,虽然我们只需要渲染在可视区域上的元素,但是我们还需要给内层的 div 设置 padding-top 和 padding-bottom ,替代那些不需要渲染的元素。由此保证虚拟列表是可以滚动的,而且滚动条位置和列表元素位置是相对应的。 padding-top 等于 startIndex 之前的元素高度和, padding-bottom 等于 endIndex 之后的元素高度和:

  • const paddingTop = startIndex * itemHeight
    const paddingBottom = (data.length - endIndex) * itemHeight
    innerContainer.setAttribute('style', `padding-top: ${paddingTop}px; padding-bottom: ${paddingBottom}px`)
    
  • 完整代码:

  • <style>.list-container {overflow: auto;border: 1px solid black;height: 500px;}
    </style><body><!-- 外部容器用来固定列表容器的高度,同时生成滚动条 --><div class="list-container"><!-- 内部容器用来装元素,高度是所有元素高度的和 --><div class="list-container-inner"></div></div><script>/** --------- 一些基本变量 -------- */const itemHeight = 60const height = 500/** --------- 生成数据 -------- */const initData = () => {const data = []for(let i = 0; i < 15; i++) {data.push({content: `内容:${i}`, height: itemHeight, color: i % 2 ? 'red' : 'yellow'})}return data}const data = initData()const contentHeight = itemHeight * data.lengthconst outerContainer = document.querySelector('.list-container')const scrollCallback = () => {// 获取当前要渲染的元素的坐标const scrollTop = Math.max(outerContainer.scrollTop, 0)const startIndex = Math.floor(scrollTop / itemHeight)const endIndex = startIndex + Math.ceil(height / itemHeight)const innerContainer = document.querySelector('.list-container-inner')// 从data取出要渲染的元素并渲染到容器中const viewData = data.slice(startIndex, endIndex + 1)innerContainer.innerHTML = ''for(let i = 0; i < viewData.length; i++) {const item = document.createElement('div')const itemData = viewData[i]item.innerHTML = itemData.contentitem.setAttribute('style', `height: ${itemData.height}px; background: ${itemData.color}`)innerContainer.appendChild(item)}// 未渲染的元素由padding-top和padding-bottom代替,保证滚动条位置正确const paddingTop = startIndex * itemHeightconst paddingBottom = (data.length - endIndex) * itemHeightinnerContainer.setAttribute('style', `padding-top: ${paddingTop}px; padding-bottom: ${paddingBottom}px`)}// 首屏渲染scrollCallback()// 监听外部容器的滚动事件outerContainer.addEventListener('scroll', scrollCallback)</script>
    </body>
    

不定高虚拟列表

  • 上面实现的是元素高度固定的虚拟列表,对于元素高度不固定的情况,虚拟列表实现起来会更复杂一些。

  • 因为我们事先不知道元素的高度,所以在监听滚动事件的过程中,需要遍历一遍列表获取每个元素的高度,由此算出 startIndex 、 endIndex 等数据。不过在第一次渲染之前,因为元素没有渲染出来,我们拿不到元素的真实高度。所以这时候需要给元素一个最小高度 itemHeight ,通过 itemHeight 来计算 startIndex 和 endIndex,由此保证第一次渲染出来的元素能占满整个可视区域。

  • 另外,当元素渲染出来以后,我们用一个字典去记录每个元素的真实高度,供下次滚动事件触发时 startIndex 和 endIndex 的计算。

  • startIndex 的算法是在遍历列表元素的过程中,逐步累加当前元素的高度得到 contentHeight ,当第一次出现 contentHeight 大于容器顶部卷起来的长度的时候,说明当前元素是列表可视区域的第一个元素,记为 startIndex。

  • endIndex 的算法是当第一次出现 contentHeight 大于 容器顶部卷起来的长度 + 容器高度 的时候,说明当前元素是列表可视区域的最后一个元素,记为 endIndex 。

  • 完整代码:

  • <style>.list-container {overflow: auto;border: 1px solid black;height: 500px;}
    </style><body><!-- 外部容器用来固定列表容器的高度,同时生成滚动条 --><div class="list-container"><!-- 内部容器用来装元素,高度是所有元素高度的和 --><div class="list-container-inner"></div></div><script>/** --------- 一些基本变量 -------- */const itemHeight = 60const height = 500/** --------- 生成数据 -------- */const getRandomHeight = () => {// 返回 [60, 150] 之间的随机数return Math.floor(Math.random() * (150 - itemHeight + 1) + itemHeight)}const initData = () => {const data = []for(let i = 0; i < 15; i++) {data.push({content: `内容:${i}`, height: getRandomHeight(), color: i % 2 ? 'red' : 'yellow'})}return data}const data = initData()const cacheHeightMap = {}const outerContainer = document.querySelector('.list-container')const scrollCallback = () => {let contentHeight = 0let paddingTop = 0let upperHeight = 0let startIndexlet endIndexconst innerContainer = document.querySelector('.list-container-inner')const scrollTop = Math.max(outerContainer.scrollTop, 0)// 遍历所有的元素,获取当前元素的高度、列表总高度、startIndex、endIndexfor(let i = 0; i < data.length; i++) {// 初始化的时候因为元素还没有渲染,无法获取元素的高度// 所以用元素的最小高度itemHeight来进行计算,保证渲染的元素个数能占满列表const cacheHeight = cacheHeightMap[i]const usedHeight = cacheHeight === undefined ? itemHeight : cacheHeightcontentHeight += usedHeightif (contentHeight >= scrollTop && startIndex === undefined) {startIndex = ipaddingTop = contentHeight - usedHeight}if (contentHeight > scrollTop + height && endIndex === undefined) {endIndex = iupperHeight = contentHeight}}// 应对列表所有元素没有占满整个容器的情况if (endIndex === undefined) {endIndex = data.length - 1upperHeight = contentHeight}// 未渲染的元素的高度由padding-top和padding-bottom代替,保证滚动条位置正确// 这里如果把设置pading的操作放在渲染元素之后,部分浏览器滚动到最后一个元素时会有问题const paddingBottom = contentHeight - upperHeightinnerContainer.setAttribute('style', `padding-top: ${paddingTop}px; padding-bottom: ${paddingBottom}px`)// 从data取出要渲染的元素并渲染到容器中const viewData = data.slice(startIndex, endIndex + 1)innerContainer.innerHTML = ''const fragment = document.createDocumentFragment()for(let i = 0; i < viewData.length; i++) {const item = document.createElement('div')const itemData = viewData[i]item.innerHTML = itemData.contentitem.setAttribute('style', `height: ${itemData.height}px; background: ${itemData.color}`)fragment.appendChild(item)}innerContainer.appendChild(fragment)// 存储已经渲染出来的元素的高度,供后面使用const children = innerContainer.childrenlet flag = startIndexfor(const child of children) {cacheHeightMap[flag] = child.offsetHeightflag++}}// 首屏渲染scrollCallback()// 监听外部容器的滚动事件outerContainer.addEventListener('scroll', scrollCallback)</script>
    </body>
  • 在这里插入图片描述

http://www.yayakq.cn/news/88794/

相关文章:

  • 网站建设创客大数据营销专业
  • asp.net做的网站要放到网上空间去要放哪些文件上去wordpress后台模板修改
  • 上传文档网站开发什么响应式网站
  • 导购网站开发网站备案都有哪些
  • 必须网站的访问量wordpress 导航 图片
  • 专题学习网站模板做渔家乐推广的有哪些好网站
  • 鹤壁建设网站推广公司电话沙田镇网站建设公司
  • 公司的网 网站打不开漫画网站模板
  • 仿淘宝网站制作厦门建设银行招聘网站
  • 郑州大学现代远程教育《网页设计与网站建设》课程考核要求黔西南建设厅网站
  • 贵阳网站建设网站制作技术开发
  • 网站维护是怎么回事互联网运营在线培训
  • 湖南易图做推广送网站网站后台文本编辑器
  • php做网站还是linux海外广告投放渠道
  • 新材料 东莞网站建设如何做宣传自己公司网站
  • 青岛市网站建设福州网络公司
  • 手机门户网站源码百度快照优化排名怎么做
  • 咖啡网站设计做网站的人 优帮云
  • 宁波 手机网站建设怎么用nas做网站服务器
  • 中国手机最好的网站排名怎么样做电影网站
  • 大理网站建设滇icp备制作动漫的软件
  • 做网站读什么专业安庆市网站建设制作
  • 杭州网站搭建多少钱江苏华东建设基础工程有限公司网站
  • 优秀网站建设哪个公司好网站美工和网页设计的区别
  • 什么网站能买建设摩托车wordpress收款插件
  • 沛县网站建立网站需要服务器吗
  • 建论坛网站做东西的网站有那些
  • 如何建设一个子网站网页小游戏斗地主
  • 网站内的地图导航怎么做的google网站打不开
  • 网站策划案范文郑州开发小程序平台