useLazyLoading: start loading when near viewport

## Issue
#6332 <Thumbnail lazy-loader is too slow || Use browser-level lazy-loading>

## Change
Switch from "threshold-based check" to "viewport distance comparison" using the `rootMargin` parameter. The root is the viewport.

This change makes it closer to the native `loading="lazy"` behavior, where it starts to load when approaching the viewport. Chrome I believe uses 3000px distance -- I think 500px is a good compromise for now. Can adjust further.

## Future
- We are currently creating N instances of IntersectionObserver.
  - https://developers.google.com/web/updates/2016/04/intersectionobserver
  - "If you need to observe multiple elements, it is both possible and advised to observe multiple elements using the same IntersectionObserver instance by calling observe() multiple times."

This would probably need a refactor to make ClaimList (or something higher) own the IntersectionObserver.
This commit is contained in:
infinite-persistence 2021-07-08 21:17:37 +08:00 committed by Thomas Zarebczan
parent 59a8b9d663
commit f7cf21b661

View file

@ -5,15 +5,24 @@ import React, { useEffect } from 'react';
/** /**
* Helper React hook for lazy loading images * Helper React hook for lazy loading images
* @param elementRef - A React useRef instance to the element to lazy load. * @param elementRef - A React useRef instance to the element to lazy load.
* @param {Number} [threshold=0.5] - The percent visible in order for loading to begin. * @param yOffsetPx - Number of pixels from the viewport to start loading.
* @param {Array<>} [deps=[]] - The dependencies this lazy-load is reliant on. * @param {Array<>} [deps=[]] - The dependencies this lazy-load is reliant on.
*/ */
export default function useLazyLoading( export default function useLazyLoading(
elementRef: { current: ?ElementRef<any> }, elementRef: { current: ?ElementRef<any> },
threshold: number = 0.25, yOffsetPx: number = 500,
deps: Array<any> = [] deps: Array<any> = []
) { ) {
const [srcLoaded, setSrcLoaded] = React.useState(false); const [srcLoaded, setSrcLoaded] = React.useState(false);
const threshold = 0.01;
function calcRootMargin(value) {
const devicePixelRatio = window.devicePixelRatio || 1.0;
if (devicePixelRatio < 1.0) {
return Math.ceil(value / devicePixelRatio);
}
return Math.ceil(value * devicePixelRatio);
}
useEffect(() => { useEffect(() => {
if (!elementRef.current) { if (!elementRef.current) {
@ -45,11 +54,12 @@ export default function useLazyLoading(
}, },
{ {
root: null, root: null,
rootMargin: '0px', rootMargin: `0px 0px ${calcRootMargin(yOffsetPx)}px 0px`,
threshold, threshold: [threshold],
} }
); );
// $FlowFixMe
lazyLoadingObserver.observe(elementRef.current); lazyLoadingObserver.observe(elementRef.current);
}, deps); }, deps);