Various virtualization improvements. Fixes #25535 (#25260)

* Use ItemSize if it's close to the calculated item size.

* Update WeatherForecastService.cs

* Improved item size calculation.

* Always use calculated item size

* Disable overflow anchoring on scroll containers (except the document itself)

* Update JS files following rebase

* Apply overflow anchor fix to document element too

* Add OverscanCount parameter

Co-authored-by: Steve Sanderson <SteveSandersonMS@users.noreply.github.com>
This commit is contained in:
Mackinnon Buck 2020-09-03 10:29:47 -07:00 committed by GitHub
parent 12c016567c
commit 8418ef66e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 24 additions and 7 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,7 +5,7 @@ export const Virtualize = {
const observersByDotNetId = {};
function findClosestScrollContainer(element: Element | null): Element | null {
function findClosestScrollContainer(element: HTMLElement | null): HTMLElement | null {
if (!element) {
return null;
}
@ -20,8 +20,14 @@ function findClosestScrollContainer(element: Element | null): Element | null {
}
function init(dotNetHelper: any, spacerBefore: HTMLElement, spacerAfter: HTMLElement, rootMargin = 50): void {
// Overflow anchoring can cause an ongoing scroll loop, because when we resize the spacers, the browser
// would update the scroll position to compensate. Then the spacer would remain visible and we'd keep on
// trying to resize it.
const scrollContainer = findClosestScrollContainer(spacerBefore);
(scrollContainer || document.documentElement).style.overflowAnchor = 'none';
const intersectionObserver = new IntersectionObserver(intersectionCallback, {
root: findClosestScrollContainer(spacerBefore),
root: scrollContainer,
rootMargin: `${rootMargin}px`,
});
@ -57,7 +63,9 @@ function init(dotNetHelper: any, spacerBefore: HTMLElement, spacerAfter: HTMLEle
return;
}
const spacerSeparation = spacerAfter.offsetTop - (spacerBefore.offsetTop + spacerBefore.offsetHeight);
const spacerBeforeRect = spacerBefore.getBoundingClientRect();
const spacerAfterRect = spacerAfter.getBoundingClientRect();
const spacerSeparation = spacerAfterRect.top - spacerBeforeRect.bottom;
const containerSize = entry.rootBounds?.height;
if (entry.target === spacerBefore) {

View File

@ -88,6 +88,15 @@ namespace Microsoft.AspNetCore.Components.Web.Virtualization
[Parameter]
public ICollection<TItem>? Items { get; set; }
/// <summary>
/// Gets or sets a value that determines how many additional items will be rendered
/// before and after the visible region. This help to reduce the frequency of rendering
/// during scrolling. However, higher values mean that more elements will be present
/// in the page.
/// </summary>
[Parameter]
public int OverscanCount { get; set; } = 3;
/// <inheritdoc />
protected override void OnParametersSet()
{
@ -251,8 +260,8 @@ namespace Microsoft.AspNetCore.Components.Web.Virtualization
_itemSize = ItemSize;
}
itemsInSpacer = Math.Max(0, (int)Math.Floor(spacerSize / _itemSize) - 1);
visibleItemCapacity = (int)Math.Ceiling(containerSize / _itemSize) + 2;
itemsInSpacer = Math.Max(0, (int)Math.Floor(spacerSize / _itemSize) - OverscanCount);
visibleItemCapacity = (int)Math.Ceiling(containerSize / _itemSize) + 2 * OverscanCount;
}
private void UpdateItemDistribution(int itemsBefore, int visibleItemCapacity)