Overscan (#64) helps UX a lot when cells contain slowly rendered graphics. However, when a user suddenly changes scroll direction, flickering still occurs. This happens because the cells are deleted immediately after going even by 1 pixel beyond the screen.
Some exploration tasks expect users to navigate around a virtual grid in unpredictable directions to perform visual comparison between objects or features. It would help a lot if developers could tell react-virtualized to keep cell cache in both directions when needed. This could be something like overscanBothDirections={true} or reverseOverscanColumnCount={42} + reverseOverscanRowCount={43}.
What do you think?
The amount of flickering that happens when a user reverses scroll direction should be _extremely_ minimal since the overscan direction will be updated after the first "scroll" event. I'm leaning toward saying that adding API complexity in this area wouldn't be worth it.
If you'd like to put together a proof of concept (a fork) I'd be happy to look at it.
OK, I'll try assembling something within a couple of weeks.
Hi again @bvaughn,
Check out this MWE:
https://kachkaev.github.io/react-virtualized-overscan-direction-issue/
Source:
https://github.com/kachkaev/react-virtualized-overscan-direction-issue/blob/master/src/App.js
When you scroll in one direction, the cells almost always render outside the viewport, because overscanRowCount=2. However, when you suddenly decide to scroll back, you start seeing green or red artifacts while the tiles or SVGs are being prepared. reverseOverscanRowCount={N} could solve this negative visual effect.
The problem I was able to reproduce in the demo is not as severe as in the real app I've been making. I've got WebGL maps with vector tiles in there and the components require 0.5-1 sec to warm up. MapboxGL.js has some issues with webpack, so I could not fit it into create-react-app for the demo (it works OK with react-boilerplate, but that would make the MWE too bulky).
Would be glad to know what you think! The visual effect that's being observed at the moment makes it harder to perform data exploration and certain cognitive comparison tasks and it'd be great to escape this problem somehow.
Sorry for the slow response time. I see what you're describing in the demo you've shared. _However_ I'm concerned that reverse over-scanning probably won't be sufficient to avoid the problem you're mentioning. I'll try to elaborate.
As _soon_ as a user starts scrolling- Grid shifts its overscan strategy in the direction being scrolled. This means that impact of a reverse-overscan would be minimal- only affecting the amount of time it took Grid to make that initial shift. It your WebGL tiles are so heavy that this is a noticeable impact- then all the user would have to do is scroll slightly faster and they're going to outrun the overscan anyway.
I think a better solution here would be to try to find a way to render a lighter-weight, faster representation of a tile if the cellRenderer's isScrolling param is true- and then render the full tile when it's false. This should greatly reduce the load while scrolling.
Admittedly, this might be far easier said than done. But I think over-scanning adjusted would be a bandaid here. Grid helps avoid rendering thousands/millions of cells so things can be fast- but if each cell is _super heavy_ then there's not much that it can do about that. Over-scanning would help a bit if users scroll slowly and pause frequently, but...it's impact would be limited.
Still happy to look at a proposal if you share one. But I'll have to weigh the complexity it would add to Grid against the fact that (I _think_) this is a fairly unique scenario. :grin:
Thanks for sharing your thoughts @bvaughn,
I completely agree with you than a reverse overscan will not give a 100% robust solution for heavy stuff like vector tiles, but I still find this prop potentially useful even in those large grids with lightweight cells you described.
I just opened my example on an iPhone and dragged the grid back and forth with a finger. What I noticed is that when I was trying to move the grid strictly horizontally or vertically, I could not do it with enough accuracy and this caused too much rendering work because the grid was constantly changing the set of cells it actually shows.
It turns out that when the direction for, let's say, x is not changing, the direction for y can switch quite frequently, because the window is unintentionally moved up and down by a few pixels all the time. When such finger-driven scrolling happens very close to a border of some row, it is possible to see the flashing content in the cells that continuously get beyond the view by just a bit and then gets back into the view.
Thus, a subtle unintentional change in a direction causes a huge change in the real DOM, especially when overscanRowCount (overscanColumnCount) is high. Although a side-effect may not be noticeable by a developer, mobile browsers will tend to drain the battery more aggressively plus there is a change that users on old devices will experience freezing during a not-so-strict horizontal or vertical finger-scrolling.
In my view, it is often more efficient to maintain more DOM nodes and shallow-render more instances of React DOM all the time than to reduce the number of components to render, but to significantly change the set being rendered, thus creating rather large diffs in the real DOM all the time.
If we could set overscanBothDirections={true} or if it was possible to set reverseOverscanRowCount={10} & overscanRowCount={10}, scrolling the grid with a finger would not create that many updates in the DOM as it happens now. As a by-product, heavy cells with maps and charts will render a bit smoother too.
I understand that this feature can be a bit hard to implement, but it's probably worth it given the popularity of the component (I wonder what the overall monthly savings in kW*h would be across the world :smile: )
This feature will be supported in the upcoming 9.0.0 release thanks to PR #579 by @clauderic.
Check out the new overscanIndicesGetter prop for Grid to learn more.
Thanks @clauderic for another great contribution and thanks @kachkaev for the thoughtful discussion. 馃槃
PS just pushed another RC (2) with this change if you'd like to test early~ npm i react-virtualized@next
Do you think that we can have this new property (overscanIndicesGetter) on the List as well? I guess it could be passed through to the Grid like is done with overscanRowCount.
P.s.:
This is great news!, I wanted to avoid the extra cpu consumption when the scroll direction is changed on small devices and this is what I needed to achieve it. Thank you very much!
List passes props through to the child Grid so you should be able to use this with List as well, yes. 馃槃
Neat!
What do you think about adding overscanIndicesGetter to the List propTypes? Also, it would be nice to have it listed on the List documentation
Either that or maybe specifying on the docs that all props given to List will be passed through to the Grid underlying component.
No reason to add it to the List propTypes if it's just a pass-thru prop.
I think adding a header note that List supports pass-thru props for Grid to the docs would be a good change. Want to PR it?
Sure! here it is https://github.com/bvaughn/react-virtualized/pull/583
Most helpful comment
Hi again @bvaughn,
Check out this MWE:
https://kachkaev.github.io/react-virtualized-overscan-direction-issue/
Source:
https://github.com/kachkaev/react-virtualized-overscan-direction-issue/blob/master/src/App.js
When you scroll in one direction, the cells almost always render outside the viewport, because
overscanRowCount=2. However, when you suddenly decide to scroll back, you start seeing green or red artifacts while the tiles or SVGs are being prepared.reverseOverscanRowCount={N}could solve this negative visual effect.The problem I was able to reproduce in the demo is not as severe as in the real app I've been making. I've got WebGL maps with vector tiles in there and the components require 0.5-1 sec to warm up. MapboxGL.js has some issues with webpack, so I could not fit it into
create-react-appfor the demo (it works OK withreact-boilerplate, but that would make the MWE too bulky).Would be glad to know what you think! The visual effect that's being observed at the moment makes it harder to perform data exploration and certain cognitive comparison tasks and it'd be great to escape this problem somehow.