rintoj / ngx-virtual-scroller

Virtual Scroll displays a virtual, "infinite" list.
https://rintoj.github.io/ngx-virtual-scroller
MIT License
977 stars 294 forks source link

Resizing page and virtual scroller table row flicker #343

Open casper5822 opened 5 years ago

casper5822 commented 5 years ago

I have a table with a virtual scroller.

Use case problem:

1 A table with the virtual scoller 2 Other elements before like divs. 3 User is at some point in the table so i have rows displayed

If the user makes a window resize, the elements before the table change their height, the virtual scroller refresh and recalculate the new size: In this operation the user notices that some rows flickers for some period during the resizing phase.

So this is my situation:

`<div class="col-xs-4"></div> 
<div class="col-xs-4"></div>
<div class="col-xs-4"></div>
<div class="col-xs-4"></div>

<virtual-scroller #scroll [items]="items"  [parentScroll]="mainPanelRef?.nativeElement">
                <table class="table">
                    <thead>
                    </thead>
                    <tbody #container> 
</tbody>
</table>
</virtual-scroller>`
speige commented 5 years ago

Could you create a StackBlitz?

casper5822 commented 5 years ago

Consider that i have images in every row. When the page is resizing and i am in a certain point of the table some rows start to go up and down in a strong way until the resize finish.

In this example i tried to recreate the "movement of the row", in my case the rows are more complex so the effect is greater.

I am not pratical with stackblitz, i don't know if this is the right share link (sorry) but the stackblitz documentation is not clear.

https://stackblitz.com/edit/create-8waosx

speige commented 5 years ago

I can see that it feels sluggish when resizing, but I don't necessarily see how the layout is incorrect. Maybe if i looked closer (or used your image example) I'd understand better.

One possible option would be to modify the checkResizeInterval setting. I'm curious if any of these values would work better for your situation? [checkResizeInterval]="0" [checkResizeInterval]="1" [checkResizeInterval]="10000"

The default is 1000 (1 second) 0 disables the refresh when the page resizes, which should render faster but the rendered layout will be less accurate 1 refreshes instantly on resize, so it'll be slower but should render more accurately. 10000 would essentially wait until they're done resizing to do 1 single refresh (assuming they don't resize for more than 10 seconds), so it should still be accurate eventually, but will render quickly & inaccurately for 10 seconds.

Let me know how those options work for you. Depending on which of these works best, we can come up with a change to the library to make it work better for everyone.

My initial thought is maybe changing it from a "throttle timeout" to a "debounce timeout", so it would essentially wait until they're done resizing before refreshing.

casper5822 commented 5 years ago

I tried your suggestion on my table, here is what i got:

Use case: Before the table there are 4 cards with col-md-3 and when the user resizes the page the cards go to col-xs-12 and make an height unshift. User is at "some point" in a table with lot of rows but not at the beginning of the table and make the resize.


1 [checkResizeInterval]="0":

On resizing i see on the headers (but i am not at the beginning) no refreshing occurs:

intervall_0

2) [checkResizeInterval]="1": No flickering, but the scrollPosition is different because of the cards resizing (before the resizing i see the row with id 120, after scrollPosition moves and i see row with id 124

Before resizing

intervall_1_pre_height

After resizing (scrollPosition is different - it is equal if i go back to the previous size of the window)

intervall_1_after_height

3) [checkResizeInterval]="10000": i see the effect 1) (headers in the middle of the table or white space on the bottom) and than after 10s the effect 2) scroll position change because of the cards.

So i think solution 1 seems to have no flickers but it depends on the specific "scrollPosition" where the resize occurs.

I also tried to remove the cards before the table. In this case, no flicker occurs and the scrollPosition remains the same because no height shifting is present, but this is obvious.

I notice that flicker occurs also when the td text has "white-space:break-all" so the text goes to a new line and some rows change their height, so i tried to set [enableUnequalChildrenSizes] = "true" but it doesn't solve the problem.

speige commented 5 years ago

It appears that [checkResizeInterval]="1" is the best option because it prevents flickering. In this case, if I converted resizeInterval to a debounce instead of a throttle, it probably wouldn't help at all. So, I think I guessed wrong :(

There is code in virtual-scroller to re-focus the previously focused item when the items array is prepended. I think we could do the same when a resize occurs.

I believe the easiest way to test this would be a change in https://github.com/rintoj/ngx-virtual-scroller/blob/master/src/virtual-scroller.ts line 880 from:

this.disposeResizeHandler = this.renderer.listen('window', 'resize', this.onScroll);

to:

this.disposeResizeHandler = this.renderer.listen('window', 'resize', () => this.refresh_internal(true));

The onScroll method is identical to refresh_internal(false) except that it's debounced/throttled. The "true" means it should re-focus the previously focused item.

Could you make that change, re-compile, copy to your node_modules, & then test to see if the issue is resolved?

casper5822 commented 5 years ago

We did a simple workaround, and for now all tests are passed. In our case we changed all the elements layout before the tables (we have just simple tables) in order to not create a height shift of the table itself. So the table is "fixed". I have to talk with my collegues and ask for try this solution because we have to "reopen" the problem so it will take some time. I will inform you