Image delay loading is very important, especially for mobile users.
The image lazy loading mechanism is simple in theory, but there are a lot of details to pay attention to in practice. In addition, there are several different use cases that benefit from lazy loading. First, let’s look at lazy loading of inline images in HTML.
Lazy loading is a method of lazily loading non-critical resources when a page is loaded, but these resources are not loaded until they are needed. In terms of graphics, “non-critical” usually means “off-screen”.
Recently, I am working on a mobile comic app (id.mangaya.mobi), which involves a large number of pictures. If the pictures are not processed in an additional way, it will not be very friendly to users, and as a result, the score of Lighthouse will be lowered.
scenario
There are two main scenarios.
For those of you interested, check out the react-progressive-lazy-image, which is very easy to use!
Senario 1: Images are delayed until they are loaded in the viewPort window.
Senario 2: Users viewing comics need to load the comics sequentially.
The principle of
The image lazy loading technique determines whether the image resource is loaded by monitoring whether the image resource container appears in the viewport area.
So the core of image lazy loading technology is how to determine the elements in the viewport region.
practice
So how do you do that?
getBoundingClientRect()
The value returned is a DOMRect object that is the union of the rectangles returned by the getClientRects() element, the CSS border associated with the element. The result is that it contains the entire element, with read-only minimum rectangles left, top, right, bottom, x, y, width, and height properties described in the pixel’s overall bounding box. Except the properties width and height relative to the upper-left corner of the viewport.
//https://gomakethings.com/how-to-test-if-an-element-is-in-the-viewport-with-vanilla-javascript/
export const elementIsInsideViewport = el => {
const bounding = el.getBoundingClientRect();
return (
bounding.top >= 0 &&
bounding.left >= 0 &&
bounding.bottom <=
(window.innerHeight || document.documentElement.clientHeight) &&
bounding.right <=
(window.innerWidth || document.documentElement.clientWidth)
);
};
Copy the code
You can use window.onScroll and window.onResize events and throttle to evaluate img elements.
document.addEventListener("DOMContentLoaded".function() {
let lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));
let active = false;
const lazyLoad = function() {
if (active === false) {
active = true;
setTimeout(function() {
lazyImages.forEach(function(lazyImage) {
if((lazyImage.getBoundingClientRect().top <= window.innerHeight && lazyImage.getBoundingClientRect().bottom >= 0) && getComputedStyle(lazyImage).display ! = ="none") {
lazyImage.src = lazyImage.dataset.src;
lazyImage.srcset = lazyImage.dataset.srcset;
lazyImage.classList.remove("lazy");
lazyImages = lazyImages.filter(function(image) {
returnimage ! == lazyImage; });if (lazyImages.length === 0) {
document.removeEventListener("scroll", lazyLoad);
window.removeEventListener("resize", lazyLoad);
window.removeEventListener("orientationchange", lazyLoad); }}}); active =false; }, 200); }}; document.addEventListener("scroll", lazyLoad);
window.addEventListener("resize", lazyLoad);
window.addEventListener("orientationchange", lazyLoad);
});
Copy the code
This code uses getBoundingClientRect in the Scroll event handler to check if there are any IMg. lazy elements in the viewport. The setTimeout call is used to delay processing, and the active variable contains the processing state, which is used to limit function calls. These elements are removed from the element array when the image is lazily loaded. The scrolling event handler code is removed when the length of the element array reaches 0.
While this code works in almost any browser, there is a potential performance problem, namely that repeated setTimeout calls can be wasteful and will still run even if the code in them is restricted. In this example, when the document is rolled or the window is resized, the check is run every 200 milliseconds, regardless of whether there is an image in the viewport. In addition, the tedious work of keeping track of the number of elements that have not yet been lazily loaded and unbinding the rolling event handler is left to the developer.
Use of best practicesIntersection Observer
This method is very simple. All it needs to do is to generate a IntersectionObserver for the element and monitor the element, and then judge the intersectionRatio ratio of the element through the callback of monitoring. This is the core code.
componentDidMount() {
const { src, needLazyUtilInViewPort, canLoadRightNow } = this.props;
if(needLazyUtilInViewPort) { //https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API try { const node = ReactDOM.findDOMNode(this); this.observer = new IntersectionObserver(this.insideViewportCb); this.observer.observe(node); } catch (err) { console.log("err in finding node", err); }}else {
if(canLoadRightNow) { this.loadImage(src); }} insideViewportCb(entries) {entries.foreach (Element => {// inside viewPortif(element.intersectionRatio >0) { this.loadImage(this.props.src); }}); }Copy the code
The downside of Intersection Observer, however, is that while it is well supported across browsers, it is not supported across all browsers. For browsers that do not support Intersection Observer, you can use Polyfill, or as described in the code above, to test whether Intersection Observer is available and fall back to a more compatible older method when it is not.
For the list diagram to load sequentially, the parent component can be notified at each image callback to load the next one. In general, both getBoundingClientRect and Intersection Observer can implement lazy loading of images. However, getBoundingClientRect will stall if it uses other onScroll events on the current page. The Intersection Observer is very simple and smooth to use.
Image lazy loading && list graph sequential loading component has been open source ~!
For those of you interested, check out the react-progressive-lazy-image, which is very easy to use!