【问题标题】:Scroll to bottom with cdk-virtual-scroll (Angular 8)使用 cdk-virtual-scroll (Angular 8) 滚动到底部
【发布时间】:2020-11-20 15:56:48
【问题描述】:

目标

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

问题

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

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

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

我已经从 Angular 阅读了这个自动调整滚动策略问题:https://github.com/angular/components/issues/10113;,但我不确定这是否能解决我的问题。

欢迎任何关于我能做什么的想法。

测试

代码沙盒https://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);
  }

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

【问题讨论】:

  • 您使用的是哪个 Angular cdk 版本?
  • @GouravGarg "@angular/cdk": "^8.2.3"

标签: javascript angular angular-material virtualscroll angular-cdk-virtual-scroll


【解决方案1】:

如果你使用 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>

我已经更新了你的sandbox

【讨论】:

  • 感谢您的回复!它与 scrollToIndex 具有相同的行为。它没有完全进入底部,它会在下面留下最后一条消息。
  • 好吧,你可以使用 hack。只需在所有项目上添加一个类,在上述事件之后找到最后一个项目,然后按照 @adarsh-thakur stackblitz.com/edit/… 使用滚动到该项目的视图
  • 试过了,不行。因为 virtual-scroll 只有 X 元素被渲染,所以当使用getElementsByClassName 时,只有那些被检索,并且它滚动到最后渲染的元素。感谢您的回答
  • 这里更新了一个沙盒codesandbox.io/s/…
  • 我看不出有什么区别:/请按照我的描述中的测试步骤进行操作
【解决方案2】:

还有一个 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));
      }
    })();
  }

【讨论】:

  • 我的 cdk 被放置在一个不共享窗口滚动的组件中。我认为这对于博客之类的东西来说是一个很好的解决方案。谢谢!
【解决方案3】:

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

  1. 视口在其ngOnInit 中异步完成自身初始化。 (请参阅this code。)因此,如果您在它完成初始化之前尝试与之交互,它会静默失败。 (这个feature request 至少会在滚动空视口时提供错误。)
  2. 没有任何内容表明初始化已完成。 (见this feature request。)

如果你在cdk-virtual-scroll-viewport 的父组件上直接在ngAfterViewInit 中调用scrollToIndex,这是行不通的。在使用scrollToIndex 之前,您必须稍等片刻。见this 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 生命周期的一个滴答声,这似乎足以让视口自行完成。

【讨论】:

    【解决方案4】:

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

    this.virtualScrollViewport.scrollIntoView(false);
    

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

    【讨论】:

      猜你喜欢
      • 2019-08-03
      • 2021-06-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-07-12
      • 1970-01-01
      • 2022-12-14
      • 2020-03-10
      相关资源
      最近更新 更多