简体   繁体   English

使用 cdk-virtual-scroll (Angular 8) 滚动到底部

[英]Scroll to bottom with cdk-virtual-scroll (Angular 8)

Goal目标

Display a list of messages and scroll to the bottom when a new message is received, even when I am at the top.显示消息列表并在收到新消息时滚动到底部,即使我在顶部。 I would like to scroll fully bottom even with elements of different heights.即使使用不同高度的元素,我也想完全滚动到底部。

Problem问题

With virtual scroll, I have to set the [itemSize] property, but for me it is not a static value:使用虚拟滚动,我必须设置[itemSize]属性,但对我来说它不是静态值:

  • When a message is too long for one line it breaks in multiple, so its height changes.当一条消息对于一行来说太长时,它会分成多个,因此它的高度会发生变化。
  • I have different types of messages with different heights (system messages for example).我有不同类型的不同高度的消息(例如系统消息)。

Also, I am using ng-content to insert a button from the parent to load previous messages.另外,我正在使用ng-content从父级插入一个按钮来加载以前的消息。 What I see is that, when _scrollToBottom is invoked, instead of taking me to the bottom, it takes me to a bit higher.我看到的是,当_scrollToBottom被调用时,它并没有将我带到底部,而是将我带到更高一点。 I suspect this is because of the different heights of elements inside virtual-scroll.我怀疑这是因为虚拟滚动中元素的高度不同。

I have read this autosize scroll strategy issue from Angular: https://github.com/angular/components/issues/10113;我已经从 Angular 阅读了这个自动调整滚动策略问题: https : //github.com/angular/components/issues/10113; but I am not sure this will solve my problem.但我不确定这会解决我的问题。

Any idea of what I could do will be welcome.欢迎任何我能做什么的想法。

Test测试

Codesandbox : https://codesandbox.io/s/angular-virtual-scroll-biwn6代码沙盒https : //codesandbox.io/s/angular-virtual-scroll-biwn6

  • When messages are loaded, scroll up.加载消息后,向上滚动。
  • Send message.发信息。 (When the new message is loaded, instead of scrolling to bottom, the virtual-scroll stops a little higher) (加载新消息时,不是滚动到底部,而是虚拟滚动停止高一点)
  • Repeat重复

Video with the error: https://gofile.io/d/8NG9HD错误视频: https : //gofile.io/d/8NG9HD


Solution解决方案

The solution given by Gourav Garg works. Gourav Garg给出的解决方案有效。 Simply by executing twice the scroll function.只需执行两次滚动功能即可。

I am doing this now:我现在这样做:


  private _scrollToBottom() {
    setTimeout(() => {
      this.virtualScrollViewport.scrollTo({
        bottom: 0,
        behavior: 'auto',
      });
    }, 0);
    setTimeout(() => {
      this.virtualScrollViewport.scrollTo({
        bottom: 0,
        behavior: 'auto',
      });
    }, 50);
  }

I think it is not very elegant but works fine.我认为它不是很优雅,但工作正常。

You can use if you are using cdk > 7如果您使用的是 cdk > 7,则可以使用

this.virtualScrollViewport.scrollToIndex(messages.length-1);

As this will move to the top of last item.因为这将移动到最后一项的顶部。 You need to call scrollIntoView for that item.您需要为该项目调用 scrollIntoView。

 this.virtualScrollViewport.scrollToIndex(this.numbers.length - 1); setTimeout(() => { const items = document.getElementsByClassName("list-item"); items[items.length - 1].scrollIntoView(); }, 10);
 <cdk-virtual-scroll-viewport #virtualScroll style="height: 500px" itemSize="90"> <ng-container *cdkVirtualFor="let n of numbers"> <li class="list-item"> {{n}} </li> </ng-container> </cdk-virtual-scroll-viewport>

I have updated your sandbox我已经更新了你的沙箱

There's an alternative to cdk scroll.有一个 cdk 滚动的替代方法。

Here's a genric version of code to scroll to a given HTML element.这是滚动到给定 HTML 元素的通用代码版本。 It can be used as a service function in angular or as a function in javascript.它可以用作 angular 中的服务函数,也可以用作 javascript 中的函数。

scroll(el: HTMLElement, behaviour: any = "smooth", block: any = "start", inline: any = "nearest") {
    el.scrollIntoView({ behavior: behaviour, block: block, inline: inline })
  }

Sample HTML:示例 HTML:

<div class="scroll-to-top" [ngClass]="{'show-scrollTop': windowScrolled}">
    Back to top<button mat-button mat-icon-button (click)="scrollToTop()">
        <mat-icon>keyboard_arrow_up</mat-icon>
    </button>
</div>

Sample Typescript component code:示例 Typescript 组件代码:

@HostListener("window:scroll")
  onWindowScroll() {
    if (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop > 100) {
      this.windowScrolled = true;
    }
    else if (this.windowScrolled && window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop < 10) {
      this.windowScrolled = false;
    }
  }
  scrollToTop() {
    (function smoothscroll() {
      var currentScroll = document.documentElement.scrollTop || document.body.scrollTop;
      if (currentScroll > 0) {
        window.requestAnimationFrame(smoothscroll);
        window.scrollTo(0, currentScroll - (currentScroll / 8));
      }
    })();
  }

My particular use case was different from the original post, but similar enough to say something.我的特定用例与原始帖子不同,但相似到足以说明一些事情。 If you are trying to scroll to the bottom of the CDK's virtual scrolling viewport using scrollToIndex , it isn't working, and you end up here, it might help you to know a few things.如果您尝试使用scrollToIndex滚动到 CDK 虚拟滚动视口的底部,则它不起作用,而您最终到达此处,它可能会帮助您了解一些事情。

  1. The viewport finishes initializing itself asynchronously in its ngOnInit .视口在其ngOnInit异步完成自身初始化。 (See this code .) So if you try to interact with it before it is finished initializing, it silently fails. (请参阅此代码。)因此,如果您尝试在它完成初始化之前与其进行交互,它会默默地失败。 (This feature request would at least provide an error when an empty viewport is scrolled.) (当滚动空视口时,此功能请求至少会提供错误。)
  2. It has nothing declaring that initialization is finished.它没有任何声明初始化完成。 (See this feature request .) (请参阅此功能请求。)

If you call scrollToIndex directly in ngAfterViewInit on cdk-virtual-scroll-viewport 's parent component, that won't work.如果您在cdk-virtual-scroll-viewport的父组件上直接在ngAfterViewInit调用scrollToIndex ,那将不起作用。 You have to wait a tick before using scrollToIndex .在使用scrollToIndex之前,您必须等待一个刻度。 See this Stackblitz .看到这个 Stackblitz The important bits:重要的位:

ngAfterViewInit(): void {
    // does nothing
    // this.viewPort.scrollTo({ bottom: 0, behavior: 'smooth' });
    this.scrollToIndex$.next();
  }

ngOnInit(): void {
    this.subscriptions.add(
      this.scrollToIndex$
        .pipe(***-->delay(0)<--***)
        .subscribe(() =>
          this.viewPort.scrollTo({ bottom: 0, behavior: 'smooth' })
        )
    );
  }

delay(0) waits one tick of the Angular lifecycle, which seems to be enough to allow the viewport to finish itself up. delay(0)等待 Angular 生命周期的一个滴答声,这似乎足以让视口自行完成。

Use can use scrollIntoView() to scroll to the bottom of virtualScrollViewport使用可以使用scrollIntoView()滚动到virtualScrollViewport的底部

this.virtualScrollViewport.scrollIntoView(false);

refer https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView参考https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM