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

珠海移动网站建设报价2024年度关键词

珠海移动网站建设报价,2024年度关键词,工程公司管理系统,专业做网站企业简介 在大数据列表的处理上,虚拟滚动是一种优化性能的有效方式。本篇文章将详细介绍两种常见的虚拟滚动实现方式:使用 transform 属性和 Intersection Observer。重点讲解如何通过 transform 属性实现高效的虚拟滚动,并对比Angular CDK中的实…

简介

在大数据列表的处理上,虚拟滚动是一种优化性能的有效方式。本篇文章将详细介绍两种常见的虚拟滚动实现方式:使用 transform 属性和 Intersection Observer。重点讲解如何通过 transform 属性实现高效的虚拟滚动,并对比Angular CDK中的实现,探讨其在渲染优化中的应用。

虚拟滚动的基本实现(以纵向滚动为例)

常见的实现方式包括使用 transformIntersection Observer
下面我们将分别介绍这两种方法,并重点讲解 transform 的实现方式。

transform

使用 transform 实现虚拟滚动,通过计算总高度、动态渲染可见数据项,并使用 transform 调整位置。

+------------------+  <--- scroll-container (viewport)
|                  |
| +--------------+ |
| | Item 101     | |  <--- transform: translateY(5000px)
| +--------------+ |
| | Item 102     | |  <--- transform: translateY(5050px)
| +--------------+ |
| | Item 103     | |  <--- transform: translateY(5100px)
| +--------------+ |
| | Item 104     | |  <--- transform: translateY(5150px)
| +--------------+ |
| | Item 105     | |  <--- transform: translateY(5200px)
| +--------------+ |
| | ...          | |
| +--------------+ |
|                  |
+------------------+

HTML

容器的高度根据数据项的总数和每个列表项的高度动态设置,使滚动条可以覆盖整个数据集。
每个列表项通过 transform:translateY 属性调整可见数据项的位置,以补足未渲染元素的空间。

<divclass="virtual-scroll-body"[style.height.px]="data.length * listItemHeight"
><divclass="virtual-scroll-item"*ngFor="let item of showList; let index = index; trackBy: trackBy"[style.height.px]="listItemHeight"[style.transform]="'translateY(' + translateHeight + 'px)'">{{ item.name }}</div>
</div>
CSS

确保容器可以垂直滚动,同时宽度固定。
每个列表项的样式设定有边框,并且没有垂直溢出。

:host {display: inline-block;height: 400px;width: 100%;overflow-y: auto;overflow-x: hidden;.virtual-scroll-item {overflow-y: hidden;border: 1px solid #eee;}
}
TS

scroll 事件监听器根据滚动位置更新 translateHeight 和 startIndex,重新计算可渲染的数据项。

public data: TransferList[] = this.dataService.generateData(100);
public listItemHeight: number = 50;
public translateHeight: number = 0;
public showList: TransferList[] = this.data.slice(0, 20);@HostListener("scroll", ["$event"])
scroll(event: Event) {const scrollTop = (event.target as HTMLElement).scrollTop;const startIndex = Math.floor(scrollTop / this.listItemHeight);this.translateHeight = startIndex * this.listItemHeight;this.showList = this.data.slice(startIndex, startIndex + 20);
}

Intersection Observer

Intersection Observer 是一个用于检测元素是否进入视口的 API,也可以用来实现虚拟滚动。它能够在目标元素进入或离开视口时触发回调,从而动态加载或卸载元素。

@ViewChild('startMarker', { static: true }) startMarker: ElementRef;
@ViewChild('endMarker', { static: true }) endMarker: ElementRef;private observer = new IntersectionObserver(entries => {entries.forEach(entry => {if (entry.isIntersecting) {// 处理元素进入视口的逻辑}});},{root: this.elementRef.nativeElement,threshold: 0.1}
);ngOnInit(): void {this.observer.observe(this.startMarker.nativeElement);this.observer.observe(this.endMarker.nativeElement);
}ngOnDestroy(): void {this.observer.disconnect();
}

由于 Intersection Observer 的复杂性和性能开销,以及在虚拟滚动场景下不完全必要,所以就不过多介绍了。如果有需要,可以专门介绍一下这个 API。其实它更适用于需要精确监控元素可见性的场景,例如懒加载图像、触发动画等。而虚拟滚动的核心需求是减少 DOM 渲染数量,提升性能,transform 在这种情况下已经能很好地满足需求。因此,大部分实现虚拟滚动时更倾向于选择 transform 这种传统且成熟的方法,Angular CDK 实现虚拟滚动也是同样采用 transform 来进行实现。

Angular CDK 虚拟滚动

上文提到,CDK 通过 transform 实现虚拟滚动。接下来,我们将深入探讨其具体实现方式,并将 CDK 的实现与之前的简单实现进行对比,以便更好地理解其中的原理。

CdkFixedSizeVirtualScroll

内容已知固定大小的虚拟滚动策略(正式版本中仅有这一种策略)
文件支持三个传入:

  • itemSize:列表中项目的大小。
  • minBufferPx:在视口之外渲染的最小缓冲区,如果缓冲区低于这个数字,将加载更多的数据项。
  • maxBufferPx:加载更多数据项时要渲染的缓冲区。

文件实现以下函数:

  • attach:将容器附加到滚动策略中,并完成首次更新视口的渲染范围。
  • onContentScrolled:当视图端口滚动时调用,具体实现为_updateTotalContentSize 函数。 onDataLengthChanged:当数据的长度发生变化时调用。
  • _updateTotalContentSize:计算容器总高度,更新视口的总内容大小。
  • updateRenderedRange:更新视口的渲染范围,根据滚动位置重新计算可渲染的数据项下标
_updateTotalContentSize:计算容器总高度

简单实现

[style.height.px]="data.length * listItemHeight"

CDK

this._viewport.setTotalContentSize(this._viewport.getDataLength() * this._itemSize);

通过将所有数据项的数量乘以每个数据项的固定高度来计算容器的总高度。

_updateRenderedRange:根据滚动位置重新计算可渲染的数据项下标。

简单实现

const scrollTop = (event.target as HTMLElement).scrollTop;
const startIndex = Math.floor(scrollTop / this.listItemHeight);

在 Angular CDK 的虚拟滚动实现中,渲染数据项的计算不仅仅是简单的滚动偏移量除以数据项的固定高度。引入缓冲区的概念后,渲染范围的调整变得更加复杂。下面是这个过程的详细解释:

获取必要的值
  • 当前已渲染的数据项范围:通常初始值为 {start: 0, end: 0} 。
  • 视口大小:根据是横向还是纵向滚动,取视口的宽度或高度。
  • 数据项的总数量。
  • 当前容器的滚动偏移量。
调整渲染范围
  • 起始缓冲区:指第一个渲染项与视口可见区域之间的距离。
    • 如果起始缓冲区小于最小缓冲区,并且当前渲染的起始项不是第一个项,说明是向上(左)滚动就需要向上(左)扩展渲染范围
      • 计算需要向上扩展的项数,确保渲染范围的起始项不小于0。
      • 更新渲染范围的结束项,确保不超过数据总长度。
  • 结束缓冲区:指最后一个渲染项与视口可见区域之间的距离。
    • 如果结束缓冲区小于最小缓冲区,并且当前渲染的结束项不是最后一个项,说明是向下(右)滚动就需要向下(右)扩展渲染范围:
      • 计算需要向下扩展的项数,确保渲染范围的结束项不超过数据总长度。
      • 更新渲染范围的起始项,确保不低于0。

这一机制确保了在用户滚动时,视口内外有足够的缓冲区,能够更加的顺滑。具体的代码实现和示意图已放在附录二和附录三中,可帮助进一步理解。

CdkVirtualScrollViewport && CdkVirtualForOf

CdkVirtualForOf 负责数据源的管理和渲染逻辑。
CdkVirtualScrollViewport 是在 CdkVirtualForOf 的帮助下虚拟滚动的视图组件,实现了以下四个功能:

  • 初始化时将视图附加到滚动策略中
  • 声明并订阅滚动事件的可观察对象
  • 管理当前在容器中可渲染的数据项
  • 管理当前容器可渲染数据项偏移量
初始化时将视图附加到滚动策略中 & 声明并订阅滚动事件的可观察对象
this.ngZone.runOutsideAngular(() =>Promise.resolve().then(() => {this._measureViewportSize();this._scrollStrategy.attach(this);this.scrollable.elementScrolled().pipe(startWith(null), auditTime(0, SCROLL_SCHEDULER), takeUntil(this._destroyed)).subscribe(() => this._scrollStrategy.onContentScrolled());this._markChangeDetectionNeeded();})
管理当前在容器中可渲染的数据项

简单实现

this.showList = this.data.slice(startIndex, startIndex + 20);

CDK

this._viewport.renderedRangeStream.pipe(takeUntil(this._destroyed)).subscribe(range => {this._renderedItems = this._data.slice(this._renderedRange.start, this._renderedRange.end);
});

通过可渲染的数据项范围来更新要显示的数据,但 CDK 通过流的方式提供了更为复杂和灵活的渲染控制。

管理当前容器可渲染数据项偏移量

简单实现

this.translateHeight = startIndex * this.listItemHeight;

CDK

this._viewport.setRenderedContentOffset(this._itemSize * newRange.start);  

通过计算第一个可渲染数据项的索引乘以每个数据项的高度来确定偏移量。

总结

在虚拟滚动的实现中,无论是简单实现还是 Angular CDK 的企业级解决方案,核心都围绕以下三点展开:

  1. 计算总高度:用于撑起滚动效果,使滚动条能够覆盖整个数据集。
  2. 实时更新可见数据项的范围:通过只渲染视口内的数据项,减少 DOM 节点的数量,提高性能。
  3. 设置偏移量:确保渲染的 DOM 元素处于正确的位置,保持用户体验的一致性。

Angular CDK 提供了更加成熟和优化的解决方案,不仅包括精细的渲染控制和灵活的配置选项,还在性能表现上有显著提升。特别是引入缓冲区的概念,使得滚动更加平滑流畅,在处理大数据量时更加得心应手。

附录

附录一:文件关系图

在这里插入图片描述

附录二:_updateRenderedRange源码

 private _updateRenderedRange() {if (!this._viewport) {return;}//获取当前渲染数据范围 默认是{start:0,end:0}const renderedRange = this._viewport.getRenderedRange();const newRange = {start: renderedRange.start, end: renderedRange.end};//获取当前视口大小 区分横向和纵向滚动 clientHeight || clientWidthconst viewportSize = this._viewport.getViewportSize();//获取总数据容量const dataLength = this._viewport.getDataLength();//当前视图滚动偏移量let scrollOffset = this._viewport.measureScrollOffset();//计算当前视图可见的索引 不一定是整数let firstVisibleIndex = this._itemSize > 0 ? scrollOffset / this._itemSize : 0;//....// 计算起始缓冲区const startBuffer = scrollOffset - newRange.start * this._itemSize;//startBuffer < this._minBufferPx:如果起始缓冲区小于最小缓冲区(_minBufferPx),意味着需要扩展渲染范围。//newRange.start != 0:如果当前渲染的起始索引不是0,才能进行扩展。if (startBuffer < this._minBufferPx && newRange.start != 0) {//计算需要扩展的项数const expandStart = Math.ceil((this._maxBufferPx - startBuffer) / this._itemSize);//newRange.start 更新渲染起始项,确保不小于0。//newRange.end 更新渲染结束项,确保不超过数据总长度。newRange.start = Math.max(0, newRange.start - expandStart);newRange.end = Math.min(dataLength,Math.ceil(firstVisibleIndex + (viewportSize + this._minBufferPx) / this._itemSize),);} else {//计算结束缓冲区const endBuffer = newRange.end * this._itemSize - (scrollOffset + viewportSize);//startBuffer < this._minBufferPx:如果起始缓冲区小于最小缓冲区(_minBufferPx),意味着需要扩展渲染范围。//newRange.end != dataLength:如果当前渲染的结束索引不是数据总长度,才能进行扩展。if (endBuffer < this._minBufferPx && newRange.end != dataLength) {//计算需要扩展的项数,公式为 Math.ceil((this._maxBufferPx - startBuffer) / this._itemSize)。const expandEnd = Math.ceil((this._maxBufferPx - endBuffer) / this._itemSize);if (expandEnd > 0) {newRange.end = Math.min(dataLength, newRange.end + expandEnd);newRange.start = Math.max(0,Math.floor(firstVisibleIndex - this._minBufferPx / this._itemSize),);}}}this._viewport.setRenderedRange(newRange);this._viewport.setRenderedContentOffset(this._itemSize * newRange.start);this._scrolledIndexChange.next(Math.floor(firstVisibleIndex));}

附录三:CDK虚拟滚动示意图
在这里插入图片描述

http://www.hkea.cn/news/406806/

相关文章:

  • 网站怎么做更新吗如何建立网页
  • 国外建设工程招聘信息网站tool站长工具
  • 专业做相册书的网站电商网站建设制作
  • 银川网站开发公司电话东莞网
  • 环境保护局网站管理制度建设百度指数的主要功能有
  • 安装wordpress提示500错误关键词优化的策略有哪些
  • 企业网站建设公司排名深圳高端seo公司助力企业
  • 做网站套餐网站seo
  • 网站上的代码网页怎么做的下载百度软件
  • 网站功能模块建设搜狗推广
  • 网站做推广有用吗网站页面设计
  • 做简报的网站广州搜发网络科技有限公司
  • 南乐县住房和城乡建设局网站制作网站的步骤是什么
  • 金华做网站最专业的公司搜易网提供的技术服务
  • wordpress适合门户网站吗怎么营销自己的产品
  • 常用的网站类型有哪些seo优化专员编辑
  • 网站专题框架怎么做海阳seo排名
  • 手机网站代码下载黄页网站推广服务
  • 做网站前端多少钱在线bt种子
  • wordpress+模版+推荐专业网站seo推广
  • 浦项建设公司员工网站2023免费推广入口
  • 如何查询某个网站的设计公司最新推广注册app拿佣金
  • 八宝山做网站公司打广告
  • wordpress vip查看插件南宁seo费用服务
  • 建站之星模板怎么设置手机如何做网站
  • 上海公司网站制作价格西安百度关键词排名服务
  • 长沙网页制作开发公司aso优化方案
  • 深圳罗湖网站制作成人电脑基础培训班
  • 无锡网站制作咨询深圳网站设计十年乐云seo
  • 大连城市建设网站seo优化顾问服务阿亮