资讯专栏INFORMATION COLUMN

图像延迟加载 && 列表图顺序加载

ghnor / 744人阅读

摘要:此外,跟踪尚未延迟加载的元素数量,以及取消绑定滚动事件处理程序的繁琐工作将由开发者来完成。图像延迟加载列表图顺序加载的组件已经开源啦有兴趣的同学可以查看使用起来非常简单

图片延时加载十分重要,尤其是对于移动端用户。

从理论上来看,图像延迟加载机制十分简单,但实际上却有很多需要注意的细节。 此外,有多个不同的用例均受益于延迟加载。 首先,我们来了解一下在 HTML 中延迟加载内联图像。

延迟加载是一种在加载页面时,延迟加载非关键资源的方法, 而这些非关键资源则在需要时才进行加载。 就图像而言,“非关键”通常是指“屏幕外”。

最近在做一个移动端漫画应用(id.mangaya.mobi),涉及的图片比较多,如果图片不做额外处理,会对用户不太友好,而且lighthouse评分也会因此降低。

场景

主要有两种场景。

有兴趣的同学可以查看react-progressive-lazy-image,使用起来非常简单!

Senario 1: 图片延时到在窗口viewport内才开始加载。

Senario 2: 用户观看漫画,需要漫画一张张的顺序加载。

原理

图片懒加载技术主要通过监听图片资源容器是否出现在视口区域内,来决定图片资源是否被加载。

那么实现图片懒加载技术的核心就是如何判断元素处于视口区域之内。

实践

那么如何实现呢?

getBoundingClientRect()

返回的值是一个DOMRect对象,它是getClientRects()元素返回的矩形的并集,即与元素关联的CSS边框。其结果是,其包含整个元件,具有只读的最小矩形left,top,right,bottom,x,y,width,和height性质描述在像素整体边界框。除视口左上角width和height相对于视口左上角的属性。

下面代码可以判断元素是否在窗口内

//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)
  );
};

可以结合window.onScroll以及window.onResize事件以及throttle来实现对img元素的判断。

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) {
              return image !== 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);
});

此代码在 scroll 事件处理程序中使用 getBoundingClientRect 来检查是否有任何 img.lazy 元素处于视口中。 使用 setTimeout 调用来延迟处理,active 变量则包含处理状态,用于限制函数调用。 延迟加载图像时,这些元素随即从元素数组中移除。 当元素数组的 length 达到 0 时,滚动事件处理程序代码随即移除。

虽然此代码几乎可在任何浏览器中正常运行,但却存在潜在的性能问题,即重复的 setTimeout 调用可能纯属浪费,即使其中的代码受限制,它们仍会运行。 在此示例中,当文档滚动或窗口调整大小时,不管视口中是否有图像,每 200 毫秒都会运行一次检查。 此外,跟踪尚未延迟加载的元素数量,以及取消绑定滚动事件处理程序的繁琐工作将由开发者来完成。

最佳实践 使用 Intersection Observer

此方法非常简单,只需要为元素生成一个IntersectionObserver,并且监听该元素,然后在监听的回调判断元素的intersectionRatio比率即可达到所需。这是核心代码.

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 => {
      //在viewport里面
      if (element.intersectionRatio >0) {
        this.loadImage(this.props.src);
      }
    });
  }

不过,Intersection Observer 的缺点是虽然在浏览器之间获得良好的支持,但并非所有浏览器皆提供支持。 对于不支持 Intersection Observer 的浏览器,您可以使用 polyfill,或者如以上代码所述,检测 Intersection Observer 是否可用,并在其不可用时回退到兼容性更好的旧方法。

至于列表图顺序加载的话,只需要在每个图片回调通知父组件可以加载下一张就可以了。 总的来说getBoundingClientRect和Intersection Observer都可以实现图片懒加载,但是getBoundingClientRect如果在当前页面使用到其他onScroll事件,会出现卡顿等问题,不能非常顺畅的滑动,而Intersection Observer使用起来非常简单流畅。

图像延迟加载 && 列表图顺序加载的组件已经开源啦~!

有兴趣的同学可以查看react-progressive-lazy-image,使用起来非常简单!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/7376.html

相关文章

  • 谈谈 <script> 标签以及其加载顺序问题,包含 defer &amp; async

    摘要:标签加载顺序如果要谈标签加载顺序问题,首先要谈的就是标签的位置,因为标签的位置对于加载顺序来说有着很重要的影响。例如标签在以上代码中,可能由于下载时间比较长,由于两个标签都是异步执行,互不干扰,因此可能就会先于执行。 谈谈 标签加载顺序的问题 这篇文章比较长,如果你耐心读完了,我会感谢你愿意在这篇文章上花费时间,也希望你有收获。 其实说起,几乎搞前端的都知道他的作用:引入 JavaS...

    alexnevsky 评论0 收藏0
  • 谈谈 <script> 标签以及其加载顺序问题,包含 defer &amp; async

    摘要:标签加载顺序如果要谈标签加载顺序问题,首先要谈的就是标签的位置,因为标签的位置对于加载顺序来说有着很重要的影响。例如标签在以上代码中,可能由于下载时间比较长,由于两个标签都是异步执行,互不干扰,因此可能就会先于执行。 谈谈 标签加载顺序的问题 这篇文章比较长,如果你耐心读完了,我会感谢你愿意在这篇文章上花费时间,也希望你有收获。 其实说起,几乎搞前端的都知道他的作用:引入 JavaS...

    tuniutech 评论0 收藏0
  • HTML&amp;&amp;CSS基础知识点整理

    摘要:并减少对样式的请求。缺点暴露了模块成员,外部可以修改模块内部状态。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。 HTML&&CSS基础知识点整理 一、WEB标准:一系列标准的集合 1. 结构(Structure):html 语言:XHTML[可扩展超文本标识语言]和XML[可扩展标记语言] 2. 表现(Preasentation):css...

    Jeff 评论0 收藏0
  • HTML&amp;&amp;CSS基础知识点整理

    摘要:并减少对样式的请求。缺点暴露了模块成员,外部可以修改模块内部状态。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。 HTML&&CSS基础知识点整理 一、WEB标准:一系列标准的集合 1. 结构(Structure):html 语言:XHTML[可扩展超文本标识语言]和XML[可扩展标记语言] 2. 表现(Preasentation):css...

    lindroid 评论0 收藏0
  • python Opencv实现停车位识别思路详解

      小编写这篇文章的一个主要目的,主要是给大家就相关的具体内容做出一些详细解答,比如了解使用python的相关知识,主要是涉及到python Opencv的一些相关知识,比如使用python Opencv实现停车位识别,就具体的内容,下面给大家详细解答下。  1.怎么去实现  (1)第一是需要用到一个处理画框的程序,将图片中的有车和无车的停车位给画出来,并且保存坐标(如果画错了,将鼠标移至要删除的...

    89542767 评论0 收藏0

发表评论

0条评论

阅读需要支付1元查看
<