美文网首页angular
用Angular写一个简单的VirtualScroll

用Angular写一个简单的VirtualScroll

作者: Sczlog | 来源:发表于2018-11-08 16:26 被阅读51次

    VirtualScroll适用于页面上存在大量数据但是只有少量需要被同时渲染的情况,可以根据滚动条的高度来实时渲染需要显示的代码,原生的javascript实现起来比较困难,算法复杂,渲染耗费的性能得不偿失,但是在目前主流前端框架中可以非常简单的实现。
    先上代码
    vscroll.component.html:

    <div #vscroll id='vscroll' style="height:400px;overflow: auto;width:400px">
      <div #placeHolder [ngStyle]="{'height.px':items.length*25}" style='position:relative'>
        <div #header style="height: 25px;position: fixed;background-color: white;z-index: 2;width:383px">
          <div *ngFor="let header of headers" style="display: inline-block" [ngStyle]='{"width":(100/headers.length) +"%"}'>{{header}}</div>
        </div>
        <div body style="z-index: 1;width:400px">
          <ng-container *ngFor="let item of items;let $index = index">
            <div *ngIf="$index>=start&&$index<start+showNumber" style="height: 25px;" [ngStyle]="{'top.px':$index*25}" style="position: absolute;width:100%">
              <div *ngFor="let header of headers" style="display: inline-block" [ngStyle]='{"width":(100/headers.length) +"%"}'>{{header + $index}}</div>
            </div>
          </ng-container>
        </div>
      </div>
    </div>
    

    vscroll.component.ts:

    import { Component, OnInit, ViewChild, ElementRef, AfterViewInit, Input, OnDestroy } from '@angular/core';
    import { fromEvent, pipe } from 'rxjs';
    import { throttleTime } from 'rxjs/operators';
    
    @Component({
      selector: 'app-vscroll',
      templateUrl: './vscroll.component.html',
      styleUrls: ['./vscroll.component.css']
    })
    export class VscrollComponent implements OnInit, AfterViewInit, OnDestroy {
    
      @ViewChild('placeHolder') placeHolder: ElementRef;
      @ViewChild('vscroll') vscroll: ElementRef;
      @Input() showNumber: number;
      public items = [];
      public headers = ['index', 'username', 'pwd'];
      public start: number;
      private timer;
      constructor() {
        for (let i = 0; i < 1000; i++) {
          this.items.push(i);
        }
        this.showNumber = this.showNumber ? this.showNumber : 16;
        this.start = 0;
      }
    
      ngOnInit() {
      }
    
      ngAfterViewInit(): void {
        this.updateShow();
        fromEvent(document.getElementById('vscroll'), 'scroll').pipe(throttleTime(200)).subscribe((e) => {
          if (this.timer) {
            clearTimeout(this.timer);
            this.timer = null;
            this.timer = setTimeout(() => {
              this.updateShow();
            }, 201);
          } else {
            this.timer = setTimeout(() => {
              this.updateShow();
            }, 201);
          }
        });
      }
    
      ngOnDestroy(): void {
        if (this.timer) {
          clearTimeout(this.timer);
          this.timer = null;
        }
      }
    
      updateShow() {
        this.start = this.vscroll.nativeElement.scrollTop / 25;
      }
    }
    
    
    实战效果(没做美化,相当的丑) DOM加载情况

    原理很简单,先利用placeholder这个div来模拟整个列表全部显示的大小,再根据absolute-relative定位将每一条数据通过top来定位在placeholder上。
    最后根据ngFor循环的index以及整个vscroll的scrollTop的来判断哪些数据在显示区域内将之显示,这样dom只需要加载并渲染需要的节点,从而优化性能。
    不过这里没有直接绑定scroll事件来触发更新显示窗体的事件,这是为了使用RxJS的throttle功能进行节流操作,每200ms只接受一次scroll事件,来节省scroll事件的开销,如果在一个throttle周期内没有发生第二次scroll事件,那么将之定位到当前位置。
    这里我同时也试过debounce,虽然debounce不需要额外设置一个timeout,但是执行的时候会有明显的延迟感除非将debounce时间设置到很短,那样就没有节流的意义了。
    如果将文件里一些数字常量换成@Input,这个就是一个足够使用的轻量级组件了,最近的几天我会尝试着去增加他的功能。

    相关文章

      网友评论

        本文标题:用Angular写一个简单的VirtualScroll

        本文链接:https://www.haomeiwen.com/subject/qguwxqtx.html