vue实现无限滚动,vue div滚动
在日常的移动网页开发中,偶尔会出现渲染长列表的场景。主要介绍vue虚拟滚动,有一定的参考价值,感兴趣的朋友可以参考一下。
目录
前言
滚动原理
实现
源代码
涉及
前言
在日常的移动网页开发中,偶尔会出现渲染长列表的场景。比如一个旅游网站,需要显示全国的城市列表,然后按照首字母A,B,C依次显示通讯录的所有名字.
一般长列表的数量都在几百的范围内,浏览器本身也足够支撑。但是一旦数量级达到上千,页面渲染过程就会明显卡顿。当数量超过数万甚至数十万时,页面可能会直接崩溃。
为了解决长列表带来的渲染压力,业界出现了相应的技术,即长列表的虚拟滚动。
虚拟滚动的本质,无论页面如何滑动,HTML文档都只是渲染当前屏幕视口中显示的几个Dom元素。
假设一个长长的列表中有10万条数据,对于用户来说,他永远只会看到屏幕上显示的十几条数据。因此,当页面滑动时,通过监控滚动事件快速切换视口中的数据,可以高度模拟滚动效果。
虚拟滚动只需要渲染几个Dom元素就可以模拟类似的滚动效果,这使得前端工程师开发几万甚至几十万的长列表成为可能。
下图是一个手机上覆盖全球所有城市的长长的列表页面(源代码贴在文末)。
滚动原理
要了解虚拟滚动的原理,先看下图。当手指向下滑动时,HTML页面也会向上滚动。
从图片标记的距离可以得出结论,当屏幕视口的上边缘与id为item的div元素的上边缘重合时,item元素距离长列表顶部的距离正好等于页面的scrollTop距离(后面计算距离时会用到这个结论)。
为了模拟逼真的滚动效果,虚拟滚动应该首先满足以下两个要求。
虚拟滚动列表的滚动条与普通列表一致。例如,该列表包含1000条数据。浏览器使用普通渲染方式时,假设滚动条需要向下滚动5000px才能到达底部。那么应用虚拟滚动条技术后,滚动条也要有同样的特性,向下滚动5000px才能到底。
虚拟滚动将只呈现上侧和下侧的视口和一些Dom元素。当滚动条向下滑动时,视图的内容将实时更新,以确保它与您正常呈现长列表时看到的一致。
为满足上述要求,html设计结构如下。wrapper是最外面的容器元素,位置设置为绝对或相对,子元素根据它来定位。
子元素。背景和。列表是虚拟滚动的关键.background是一个空的div,但是需要设置高度,这个高度等于长列表中所有列表项的高度之和。另外,应该设置为绝对定位,z-index的值应该设置为-1。list负责动态渲染视口观察到的Dom元素,位置设置为absolute。
模板
div class=wrapper
div class= background :style= { height:` $ { total _ height } px `} /div
div class=list
div class=line
北京/div
北京/div
/div
div class=line
上海/div
上海/div
/div
div class=line
广州/div
广州/div
/div
.//省略
/div
/div
/模板
style lang=scss 范围。包装{
位置:绝对;
左:0;
右:0;
底部:0;
顶配:60px
溢出-y:滚动;背景{
位置:绝对;
top:0;
左:0;
右:0;
z索引:-1;
}。列表{
位置:绝对;
top:0;
左:0;
右:0;
}
}
/风格
如果上面的代码total_height等于10000px,页面运行效果图如下所示。
作为子元素的高度。背景被设置,父元素。子元素将支持包装,并将出现一个滚动条。
如果此时向下滑动,两个子元素。背景和。列表将同时向上滚动。当滚动距离达到9324px时,滚动条也到达底部。
这是因为父元素的高度。wrapper本身就是676px,加上9324px的滑动距离,结果正好等于10000px的列表总高度。
通过观察上述行为,可以看出。背景只是一个空的div,但是通过赋予它列表的总高度,右边的滚动条可以保持和普通长列表渲染生成的滚动条一样的外观和行为。
滚动条问题已经解决,但是当滚动条向下滑动时,数据列表向上移动。列表移出屏幕后,下一张幻灯片全是白色的。
为了解决白屏问题,视口必须始终显示滑动数据。然后。列表元素应根据滑动距离动态更新其绝对上限值,以确保。列表不在屏幕上绘制。同时,应该在当前视口中显示的数据应该根据滑动距离进行动态渲染。
观察下面的动态图,右边的Dom结构显示了滑动时的变化。
滚动条快速向下滑动后,列表的Dom元素被快速渲染和刷新。此时,除了不断替换。列表。列表元素本身也在不断修改transform:translate 3d(0,Px,0) style值(修改translate3d可以达到和修改top属性值类似的效果)。
经过以上解释,虚拟滚动的实现逻辑已经清晰。首先,js监视滚动条的滑动事件,然后计算。列表元素通过滑动距离来呈现,然后更新。列表元素。滚动条不断滑动时,子元素和位置不断更新,在视口上模拟滚动效果。
实现
的演示页面如下图所示。列表项包含以下三种结构:
小列表项,城市首字母单行,高度50px
常用列表项,左边英文名字,右边中文名字,高度100px
大列表项,左边是英文名,中间是中文名,右边是图片,高度150px
列表city_data的json结构如下所示。类型1表示使用小列表项的样式结构呈现,类型2表示普通列表项,类型3表示大列表项。
[{name: a , value : , type: 1},{name: all ayn , value: ain , type:2},{name:Aana , value
City_data包含一个长列表中的所有数据。获得city_data后,首先遍历并调整各项的数据结构(代码如下)。
通过下面的方法,每个列表项最终包含一个top和height值。top表示从长列表顶部开始的项目长度,而height值指的是项目的高度。
Total_height是整个列表的总高度。最后将给出上述背景元素。处理后的数据会给this.list存储,并记录最小列表项this.min_height的高度。
已安装(){
get height(type){//根据类型值返回高度。
开关(类型){
案例一:返回50;
案例二:返回100;
案例三:退货150;
默认值:
返回“”;
}
}
设total _ height=0;
const list=city_data.map((data,index)={
const height=get height(data . type);
const ob={
索引,
身高,
top:总高度,
数据
}
total_height=高度;
返回ob;
})
this . total _ height=total _ height;//列表的总高度
this.list=list
this.min _ height=50//最小高度为50
//屏幕可以容纳的列表项的最大数量。containerHeight是父容器的高度,根据最小高度计算。
this . maxnum=math . ceil(container height/this . min _ height);
}
根据HTMLType值呈现不同的样式结构(代码如下)。父容器。包装器将滑动事件绑定到Scroll和list元素上。list不遍历this.list数组,因为this.list是原始数据并包含所有列表项。
Template只需要遍历需要在viewport中显示的数据runList,数组runList中包含的数据会随着滚动事件不断更新。
模板
div class= wrapper ref= wrapper @ scroll= on scroll
div class= background :style= { height:` $ { total _ height } px `} /div
div class=list ref=container
div v-for=运行列表中的项:class=[line ,get class(item . data . type)]:key= item
div class= item lt“{ item . data . name } }/div
div class= item gt“{ item . data . value } }/div
div v-if= item . data . type==3 class= img-container
img src=././assets/default.png /
/div
/div
/div
/div
/模板
事件触发onScroll方法(代码如下)。由于滚动条的触发频率很高,为了减少浏览器的计算量,使用requestAnimationFrame函数对其进行节流。
滚动事件对象e可以得到当前滚动条滑动的距离。根据距离,您可以计算运行列表的列表数据并修改的位置信息。列表。
onScroll (e) {
如果(this.ticking) {
返回;
}
this.ticking=true
requestAnimationFrame(()={
this.ticking=false
})
const distance=e . target . scroll top;
this.distance=距离;
this.getRunData(距离);
}
如何根据滚动距离快速找到屏幕视口下应该呈现的第一个列表项元素?
This.list是一个长列表的数据源,其中每个列表项都存储了它与长列表顶部的距离和它的高度。
如上所述,在页面滚动的过程中,如果视口的上边缘与一个列表项的上边缘重合,那么滑动距离scrollTop就正好等于列表项顶部到长列表顶部的距离。
然后,如果页面向上移动一点,视口下的第一个列表项只显示一部分,另一部分被绘制到屏幕之外,不可见。此时,我们仍然确定视口下的起始元素仍然是列表项,除非它继续向上移动,直到完全从屏幕上移除。
那么判断viewport中呈现的第一个元素的标准就是页面scrollTop的滚动距离在列表元素的顶部和顶部高度之间。
根据以上原理,我们可以使用二分法实现快速查询(代码如下)。
//二分法计算起始索引,scrollTop是滚动距离。
getStartIndex (scrollTop) {
设start=0,end=this . list . length-1;
while(开始结束){
const mid=Math.floor((开始结束)/2);
const { top,height }=this . list[mid];
if (scrollTop=top scrollTop顶部高度){
开始=中期;
打破;
} else if (scrollTop=顶部高度){
start=mid 1;
} else if (scrollTop top) {
end=mid-1;
}
}
返回开始;
}
diversity计算this.list数组中viewport下呈现的第一个元素的索引,该索引被命名为起始索引start_index。接下来,它转到核心函数getRunData(代码如下)。它主要做以下两件事。
更新动态运行列表数据
更新动态的位置。列表长列表元素。
实际上,假设屏幕高度为1000px,最小的列表项为50px,屏幕可以容纳的列表项this.maxNum的最大数量为20。
根据滑动距离,计算起始索引start_index,然后根据start_index从数据源this.list中截取20个元素,给this.runList,完成数据更新不是很有必要吗?
如果this.runList只容纳刚好一个屏幕的最大数量,滚动条快速滚动时,界面的渲染速度会跟不上手指滑动的速度,底部会出现白屏闪烁。
这个问题的解决方案是在HTML文档上呈现更多的缓冲数据。比如下面的getRunData函数,会渲染出可以容纳三个屏幕高度的列表项个数,分别对应顶屏、中屏、底屏。
中屏是当前视口对应的屏幕,上屏和下屏存储的是视口上下两侧不显示的缓冲Dom。首先,通过二分法可以找到屏幕视口下的第一个列表项元素索引start_index,因此根据start_index也可以很容易地得到上下屏幕的第一个列表项索引。
getRunData (distance=null) {
//滚动距离
const scrollTop=距离?距离:这个。$ refs . container . scroll top;
//在哪个范围内不进行滚动?
if (this.scroll_scale) {
if(scroll top this . scroll _ scale[0]scroll top this . scroll _ scale[1]){
返回;
}
}
//开始索引
设start _ index=this . getstartindex(scroll top);
start _ index=start _索引0?0:start _ index;
//上一个屏幕的索引,this.cache_screens默认为1,缓存一个屏幕。
let upper _ start _ index=start _ index-this . maxnum * this . cache _ screens;
upper _ start _ index=upper _ start _ index 0?0:upper _ start _ index;
//调整偏移量
这个。$ refs . container . style . transform=` translate 3d(0,${this.list[upper_start_index].top}px,0)`;
//中间屏幕的元素
const mid _ list=this . list . slice(start _ index,start _ index this . maxnum);
//在屏幕上
const upper _ list=this . list . slice(upper _ start _ index,start _ index);
//下部屏幕元素
let down _ start _ index=start _ index this . maxnum;
down _ start _ index=down _ start _ index this . list . length-1?this . list . length:down _ start _ index;
this . scroll _ scale=[this . list[math . floor(upper _ start _ index this . maxnum/2)]。top,this . list[math . ceil(start _ index this . maxnum/2)]。top];
const down _ list=this . list . slice(down _ start _ index,down _ start _ index this . maxnum * this . cache _ screens);
this.runList=[.upper_list,mid_list,down _ list];
}
滚动事件的频率非常高。作为开发者,我们应该尽量减少浏览器的运算量。所以可以在组件中缓存一个滚动范围,也就是数组this.scroll_scale(数据结构类似于[5000,5675])。当滑动距离在此范围内时,浏览器不需要更新列表数据。
滚动距离scrollTop一旦进入滚动范围,getRunData函数就什么都不做了。当手指滑动时,它使用默认的滚动行为使。用手指上下移动列表元素。
假设滚动方向是向下的,当scrollTop跑出滚动范围和滑动视口的上边缘时。wrapper与下一个列表项的上边缘重合,getRunData函数首先计算开始索引,然后通过start_index获得上一个屏幕的第一个元素索引upper _ start_index。
因为每个列表项都缓存了它与长列表顶部的距离,所以您可以获取。应由this.list [upper _ start _ index]给出的列表元素。顶端。然后,您可以重新计算新的列表数据runList呈现页面,并缓存新状态下的滚动范围。
至此,通过以上步骤实现了虚拟滚动。虽然上面介绍的实用方法使用起来很简单,但前提是设计师在规划设计稿的时候,首先要明确不同样式列表项的高度。
如果列表项的高度需要根据里面的内容自然打开,在页面设计中无法固定,可以阅读下面的参考文章来实现。
虽然在高度自适应列表项的情况下虚拟滚动听起来很吸引人,但是它需要增加额外的处理步骤,并且面临新的问题(例如当列表项包含异步加载的图片时,高度计算将变得困难),而且它还会大大增加浏览器的计算量。所以设计稿的列表项是否需要定义高度,要看具体场景。
源代码
源代码
参考
以高性能呈现100,000条数据
新手也能看懂的一种虚拟滚动方法。
浅谈虚拟链表的实现原理
这就是这篇关于vue轻松实现虚拟滚动的示例代码的文章。更多相关vue虚拟滚动内容,请搜索我们之前的文章或者继续浏览下面的相关文章。希望大家以后能多多支持我们!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。