我想要解决的问题是能够使用其索引以编程方式滚动到某个项目。示例 API 是:
async scrollToIndex(viewPort: CdkVirtualScrollViewport, index: number): Promise<boolean>
以下是我正在使用的方法的高级概述:
offset + (N * itemSize)
猜测索引 N 的偏移量。由于存在不同的 itemSize,因此该猜测预计不准确。const range = viewport.getRenderedRange()
N < range.start
- 猜测太高,纠正偏移并转到 2N >= range.end
- 猜测太低,纠正偏移并转到 2range.start <= N < range.end
- 猜测足够好,可以将 N 带入视口,转到 5这种方法有效,但存在一些问题:
guess = this.getGuess(index, offset, itemHeight);
viewPort.scrollToOffset(guess);
await delayFor(400); // ARBITRARY DELAY
const renderedRange = viewPort.getRenderedRange();
我需要它在不同的移动设备上工作,这些设备具有不同的性能特征,所以我不能使用任意延迟,它可以在我的电脑/设备上工作,但对于其他一些设备,延迟可能不够。 我正在寻找一种没有硬编码延迟的更好解决方案。例如等待一些事件,表明在
scrollToOffset()
调用后范围已更改。
关于问题2,我不确定为什么会发生,因此不知道如何解决它。我对解决第一个问题更感兴趣,但感谢任何帮助。在示例中,滚动到索引 2 给出了错误的结果。
我创建了一个最小的示例here - 在这个示例中,每个第三个列表元素都比其他元素高。
要解决您的问题,请尝试这种方法,不要造成不必要的延迟,并更有效地处理滚动。另外,对于元素未滚动到顶部的问题,请使用scrolledIndexChange事件来跟踪视口何时完成滚动,然后将元素滚动到视图中。
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
async function scrollToIndex(viewport: CdkVirtualScrollViewport, index: number): Promise<boolean> {
// Step 1: Make a guess for the offset
const itemHeight = getItemHeight(index); // Implement a function to get the item height based on the index
const guess = index * itemHeight;
// Step 2: Scroll to the guessed offset
viewport.scrollToOffset(guess);
// Step 3: Wait for the viewport to finish scrolling
await new Promise(resolve => {
const subscription = viewport.scrolledIndexChange.subscribe(() => {
subscription.unsubscribe();
resolve();
});
});
// Step 4: Get the rendered range
const renderedRange = viewport.getRenderedRange();
// Step 5: Check if the item is in the viewport
if (index < renderedRange.start) {
// The guess was too high, correct offset and scroll again
return scrollToIndex(viewport, index);
} else if (index >= renderedRange.end) {
// The guess was too low, correct offset and scroll again
const correctedOffset = (index + 1) * itemHeight - viewport.getViewportSize().height;
viewport.scrollToOffset(correctedOffset);
return scrollToIndex(viewport, index);
} else {
// The guess is good enough, scroll the element into view
const element = viewport.elementRef.nativeElement.querySelector(`[cdkvirtualforitemindex="${index}"]`);
if (element) {
element.scrollIntoView({ behavior: 'auto', block: 'start', inline: 'nearest' });
return true;
}
return false;
}
}
// Example usage:
const indexToScroll = 2; // Replace with the desired index
await scrollToIndex(yourViewportInstance, indexToScroll);