You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I want to discuss an ability to add more configuration to the viewabilityConfig, so that it's possible to more effectively control the calculations of viewable items of the VirtualizedLists.
Details
Let's bring up an example.
We have a FlatList of images;
We have a viewabilityConfig with itemVisiblePercentThreshold: 15;
We have an absolutely positioned View above the FlatList. This View is serving a purpose of some custom header and we add paddingTop: HEADER_HEIGHT to the contentContainerStyle of the FlatList so that we see all of the list;
Every time an image becomes "not visible" we want to make it slightly opaque.
Here is the result:
We can see that the image AT THE BOTTOM is getting slightly opaque, when less than 15% of it is not visible. However, we can't say the same thing about the images on top of the screen. (couldn't upload gifs for some reason so bear with my screenshots)
Why is this happening? Because our header is an absolute View and the image is continuing to be visible underneath the header. And FlatList cannot correctly computate the offset from top.
In order to solve this problem, I suggest to make the next changes, which are going to allow FlatList to correctly work with absolute Views on top or below the screen:
In ViewabilityConfig (@react-native/virtualized-lists/Lists/VirtualizedList.d.ts) interface we add absoluteStartOffset and absoluteEndOffset:
export interface ViewabilityConfig {
/**
* Minimum amount of time (in milliseconds) that an item must be physically viewable before the
* viewability callback will be fired. A high number means that scrolling through content without
* stopping will not mark the content as viewable.
*/
minimumViewTime?: number | undefined;
/**
* Percent of viewport that must be covered for a partially occluded item to count as
* "viewable", 0-100. Fully visible items are always considered viewable. A value of 0 means
* that a single pixel in the viewport makes the item viewable, and a value of 100 means that
* an item must be either entirely visible or cover the entire viewport to count as viewable.
*/
viewAreaCoveragePercentThreshold?: number | undefined;
/**
* Similar to `viewAreaCoveragePercentThreshold`, but considers the percent of the item that is visible,
* rather than the fraction of the viewable area it covers.
*/
itemVisiblePercentThreshold?: number | undefined;
/**
* Nothing is considered viewable until the user scrolls or `recordInteraction` is called after
* render.
*/
waitForInteraction?: boolean | undefined;
/**
* Offset from the start of the list
*/
absoluteStartOffset?: number | undefined; // <-- add this
/**
* Offset from the end of the list
*/
absoluteEndOffset?: number | undefined; // <-- and this
}
In computeViewableItems (@react-native/virtualized-lists/Lists/ViewabilityHelper.js) we make the next changes:
/**
* Determines which items are viewable based on the current metrics and config.
*/
computeViewableItems(
props: CellMetricProps,
scrollOffset: number,
viewportHeight: number,
listMetrics: ListMetricsAggregator,
// Optional optimization to reduce the scan size
renderRange?: {
first: number,
last: number,
...
},
): Array<number> {
...
const absoluteStartOffset = this._config.absoluteStartOffset ?? 0; // <-- add this
const absoluteEndOffset = this._config.absoluteEndOffset ?? 0; // <-- add this
const top = Math.floor(metrics.offset - scrollOffset - absoluteStartOffset); // <-- change here
const bottom = Math.floor(top + metrics.length);
if (top < viewportHeight - absoluteStartOffset && bottom > 0) { // <-- change here
firstVisible = idx;
if (
_isViewable(
viewAreaMode,
viewablePercentThreshold,
top,
bottom,
viewportHeight - absoluteStartOffset - absoluteEndOffset, // <-- change here
metrics.length,
)
) {
viewableIndices.push(idx);
}
Now, if I pass my absoluteStartOffset to the viewabilityConfig - we will see the next result:
We can see that both TOP and BOTTOM images are opaque, which means that viewability calculations are correct now.
We can also make it work with absolute views, located at the bottom of the screen by utilising absoluteEndOffset:
You may say that it's my fault that I'm using an absolute view as a header for some reason, but there are real-life scenarios where this is being used with more complicated UI than I showed in the examples above (the reason why I suggest this change is exactly because it fixes the problem for my application which has some animations on top and rebuilding UI is not as easy as simply adding these changes).
Adding custom getItemLayout with different offset is not a solution to this problem.
Introduction
I want to discuss an ability to add more configuration to the viewabilityConfig, so that it's possible to more effectively control the calculations of viewable items of the VirtualizedLists.
Details
Let's bring up an example.
paddingTop: HEADER_HEIGHT
to the contentContainerStyle of the FlatList so that we see all of the list;Here is the result:
We can see that the image AT THE BOTTOM is getting slightly opaque, when less than 15% of it is not visible. However, we can't say the same thing about the images on top of the screen. (couldn't upload gifs for some reason so bear with my screenshots)
Why is this happening? Because our header is an absolute View and the image is continuing to be visible underneath the header. And FlatList cannot correctly computate the offset from top.
In order to solve this problem, I suggest to make the next changes, which are going to allow FlatList to correctly work with absolute Views on top or below the screen:
In ViewabilityConfig (@react-native/virtualized-lists/Lists/VirtualizedList.d.ts) interface we add absoluteStartOffset and absoluteEndOffset:
In
computeViewableItems
(@react-native/virtualized-lists/Lists/ViewabilityHelper.js) we make the next changes:Now, if I pass my absoluteStartOffset to the viewabilityConfig - we will see the next result:
We can see that both TOP and BOTTOM images are opaque, which means that viewability calculations are correct now.
We can also make it work with absolute views, located at the bottom of the screen by utilising absoluteEndOffset:
You may say that it's my fault that I'm using an absolute view as a header for some reason, but there are real-life scenarios where this is being used with more complicated UI than I showed in the examples above (the reason why I suggest this change is exactly because it fixes the problem for my application which has some animations on top and rebuilding UI is not as easy as simply adding these changes).
Adding custom getItemLayout with different offset is not a solution to this problem.
This solution also works for horizontal lists.
Here is the full gist of example: https://gist.github.com/IslamRustamov/653747e65be5f59dcce953345f3dff2a
The idea of this came from https://github.com/fredrikolovsson/react-native-viewability-tracking-view
Discussion points
The text was updated successfully, but these errors were encountered: