7

目标

显示消息列表并在收到新消息时滚动到底部,即使我在顶部。即使使用不同高度的元素,我也想完全滚动到底部。

问题

使用虚拟滚动,我必须设置[itemSize]属性,但对我来说它不是静态值:

  • 当一条消息对于一行来说太长时,它会分成多行,因此它的高度会发生变化。
  • 我有不同类型的不同高度的消息(例如系统消息)。

另外,我正在使用ng-content从父级插入一个按钮来加载以前的消息。我看到的是,当_scrollToBottom被调用时,不是把我带到底部,而是把我带到更高的地方。我怀疑这是因为虚拟滚动中元素的高度不同。

我已经从 Angular 阅读了这个 autosize 滚动策略问题:https://github.com/angular/components/issues/10113;但我不确定这会解决我的问题。

任何关于我能做什么的想法都将受到欢迎。

测试

Codesandboxhttps ://codesandbox.io/s/angular-virtual-scroll-biwn6

  • 加载消息后,向上滚动。
  • 发信息。(加载新消息时,虚拟滚动不会滚动到底部,而是会停止更高一点)
  • 重复

有错误的视频:https ://gofile.io/d/8NG9HD


解决方案

Gourav Garg给出的解决方案有效。只需执行两次滚动功能。

我现在正在这样做:


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

我认为它不是很优雅,但效果很好。

4

4 回答 4

6

如果您使用 cdk > 7,则可以使用

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

因为这将移动到最后一项的顶部。您需要为该项目调用 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>

我已经更新了你的沙盒

于 2020-11-23T16:52:14.970 回答
1

cdk 滚动有一个替代方案。

这是滚动到给定 HTML 元素的通用代码版本。它可以用作 Angular 中的服务函数或 javascript 中的函数。

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

示例 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>

示例 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));
      }
    })();
  }
于 2020-11-30T06:47:31.533 回答
1

我的特定用例与原始帖子不同,但足以说明问题。如果您尝试使用 滚动到 CDK 的虚拟滚动视口的底部scrollToIndex,则它不起作用,并且您最终到了这里,它可能会帮助您了解一些事情。

  1. 视口在其ngOnInit. (请参阅此代码。)因此,如果您在它完成初始化之前尝试与之交互,它会静默失败。(当滚动空视口时,此功能请求至少会提供错误。)
  2. 它没有任何声明初始化已完成。(请参阅此功能请求。)

如果您scrollToIndex直接调用ngAfterViewInitcdk-virtual-scroll-viewport父组件,那将不起作用。在使用之前,您必须稍等片刻scrollToIndex。看到这个 Stackblitz。重要的位:

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)等待 Angular 生命周期的一个滴答声,这似乎足以让视口自行完成。

于 2021-10-08T18:07:15.910 回答
-1

使用 可以使用scrollIntoView()滚动到底部virtualScrollViewport

this.virtualScrollViewport.scrollIntoView(false);

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

于 2020-11-24T15:19:14.300 回答