ToB企服应用市场:ToB评测及商务社交产业平台

标题: 学会前端假造滚动,看这篇就够了 [打印本页]

作者: 罪恶克星    时间: 2024-10-5 19:13
标题: 学会前端假造滚动,看这篇就够了
一. 假造滚动是什么?

前端假造滚动是一种用于优化长列表或大量数据展示的技术。它的重要原理是只渲染用户当前可见区域的数据,而不是一次性渲染整个列表或数据集。
在传统的列表渲染中,如果数据量很大,渲染所有数据可能会导致性能下降,特别是在滚动时。假造滚动通过动态盘算可见区域的位置和需要渲染的数据范围,只渲染可见区域内的部分数据,并在滚动时及时更新渲染的内容。
总结下就是:
二. 假造滚动怎么实现?

先上代码直观体验下
index.vue
  1. <template>
  2.   <div class="container">
  3.     <my-virtual-scroller :items="list" />
  4.   </div>
  5. </template>
  6. <script>
  7. import myVirtualScroller from './myVirtualScroller.vue'
  8. // 模拟一个长列表
  9. const list = []
  10. for(let i=0; i<10000; i++) {
  11.   list.push({
  12.     id: i,
  13.     label: `virtual-list ${i}`
  14.   })
  15. }
  16. export default {
  17.   components: {
  18.     myVirtualScroller
  19.   },
  20.   data() {
  21.     return {
  22.       list: list
  23.     }
  24.   }
  25. }
  26. </script>
  27. <style scoped>
  28. .container {
  29.   height: 300px;
  30.   border: 1px solid #ccc;
  31. }
  32. </style>
复制代码
myVirtualScroller.vue
  1. // 传入数据可以实现虚拟滚动事件
  2. // 这是一个封装好的子组件 传入数组进行渲染
  3. <template>
  4.   <!-- 最底层的可视区容器 -->
  5.   <div ref="list" class="infinite-list-container" @scroll="scrollEvent($event)">
  6.     <!-- 中间的可滚动区域,z-index=-1,高度和真实列表相同,目的是出现相同的滚动条 -->
  7.     <div
  8.       class="infinite-list-phantom"
  9.       :style="{ height: listHeight + 'px' }"
  10.     ></div>
  11.     <!-- 最上层的可视区列表,数据和偏移距离随着滚动距离的变化而变化 -->
  12.     <div class="infinite-list" :style="{ transform: getTransform }">
  13.       <div
  14.         class="infinite-list-item"
  15.         v-for="item in visibleData"
  16.         :key="item.id"
  17.         :style="{ height: itemSize + 'px' }"
  18.       >
  19.         {{ item.label }}
  20.       </div>
  21.     </div>
  22.   </div>
  23. </template>
  24. <script>
  25. export default {
  26.   name: "MyVirtualList",
  27.   props: {
  28.     //父组件传过来的数据
  29.     items: {
  30.       type: Array,
  31.       default: () => [],
  32.     },
  33.     //可视区域每一项的高度 没有传参的话默认高度是100px
  34.     itemSize: {
  35.       type: Number,
  36.       //没有高度的话 高度会默认是100px
  37.       default: 100,
  38.     },
  39.   },
  40.   computed: {
  41.     //列表总高度
  42.     listHeight() {
  43.       // 计算当前传值数据的总长度 * 每个盒子的高度 计算出所有数据的整体高度
  44.       return this.items.length * this.itemSize;
  45.     },
  46.     //可视区列表的项数
  47.     visibleCount() {
  48.       return Math.ceil(this.screenHeight / this.itemSize);
  49.     },
  50.     //可视区列表偏移距离对应的样式
  51.     getTransform() {
  52.       return `translate3d(0,${this.startOffset}px,0)`;
  53.     },
  54.     //获取可视区列表数据
  55.     visibleData() {
  56.       // 截取出可视区域显示的盒子 进行渲染
  57.       return this.items.slice(
  58.         this.start,
  59.         Math.min(this.end, this.items.length)
  60.       );
  61.     },
  62.   },
  63.   mounted() {
  64.     // 获取可是区域的高度
  65.     this.screenHeight = this.$refs.list.clientHeight;
  66.     // 开始
  67.     this.start = 0;
  68.     // 结束
  69.     this.end = this.start + this.visibleCount;
  70.   },
  71.   data() {
  72.     return {
  73.       screenHeight: 0, //可视区域高度
  74.       startOffset: 0, //偏移距离
  75.       start: 0, //起始索引
  76.       end: 0, //结束索引
  77.     };
  78.   },
  79.   methods: {
  80.     scrollEvent() {
  81.       //当前滚动条滚动位置
  82.       let scrollTop = this.$refs.list.scrollTop;
  83.       //此时的开始索引 使用当前滚动条高度 / 盒子高度 拿到的是当前是哪一个盒子为第一个
  84.       this.start = Math.floor(scrollTop / this.itemSize);
  85.       //此时的结束索引
  86.       //获取到当前第一个盒子的数值 加 计算好可视区域可容纳多少盒子的值
  87.       this.end = this.start + this.visibleCount;
  88.       //此时的偏移距离
  89.       this.startOffset = scrollTop - (scrollTop % this.itemSize);
  90.   
  91.     },
  92.   },
  93. };
  94. </script>
  95. <style scoped>
  96. .infinite-list-container {
  97.   height: 100%;
  98.   overflow: auto;
  99.   position: relative;
  100. }
  101. .infinite-list-phantom {
  102.   position: absolute;
  103.   left: 0;
  104.   top: 0;
  105.   right: 0;
  106. }
  107. .infinite-list {
  108.   left: 0;
  109.   right: 0;
  110.   top: 0;
  111.   position: absolute;
  112. }
  113. .infinite-list-item {
  114.   line-height: 50px;
  115.   text-align: center;
  116.   color: #555;
  117.   border: 1px solid #ccc;
  118.   box-sizing: border-box;
  119. }
  120. </style>
复制代码
假造滚动的思路总结如下
1. 确定视窗位置:通过获取列表滚动区域的scrollTop属性,确定当前视窗的位置;
2. 盘算需要渲染的节点:根据视窗位置和子节点的高度,盘算出需要真实渲染的节点范围。可以使用Math.floor(scrollTop/itemHeight)和Math.ceil(viewHeight/itemHeight)来确定起始索引和渲染的节点数量;
3. 移动渲染的节点到视窗内:将需要渲染的节点移动到视窗内,可以通过修改节点的位置或使用其他方式实现;
4. 按需加载数据:根据滚动位置,动态加载相应的数据,只加载当前视窗可见范围内的数据,减少数据量和渲染负担;
5. 处理滚动变乱:监听滚动变乱,根据滚动位置及时更新需要渲染的节点和数据。
三. 假造滚动示例代码难点解析

1. 可视化容器为什么要包裹两个盒子,并且设置绝对定位?
第一个盒子(类名为"infinite-list-phantom")的作用:
其总高度为内容的总高度,重要要为了撑起父盒子,使父盒子有滚动条。
第二个盒子(类名为‘infinite-list’)的作用:
这个要可视区域,是用户所能看到的界面,也是数据渲染的界面。
至于为什么要设置绝对定位,是由于需要第二个盒子覆盖第一个盒子,以满意第二个盒子显如今可视界面。
2. 为什么要设置:style=“{ transform: getTransform }”?
原因如下:
3.为什么要设置偏移距离?
相关代码如下:
  1. //此时的偏移距离
  2. this.startOffset = scrollTop - (scrollTop % this.itemSize);
复制代码
原因如下:

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4