天天看點

利用IntersectionObserver完成懶加載、加載更多

 IntersectionObserver  (交叉觀察器)

​IntersectionObserver​

​提供了一種異步觀察目标元素與其祖先元素或頂級文檔視窗(viewport)交叉狀态的方法。祖先元素與視窗(viewport)被稱為根(root)。

​IntersectionObserver​

​對象被建立以後,其被配置為監聽根中一段給定比例的可見區域。一旦IntersectionObserver被建立,則無法更改其配置,是以一個給定的觀察者對象隻能用來監聽可見區域的特定變化值;可以在同一個觀察者對象中配置監聽多個目标元素。

var obj = new IntersectionObserver(callback, options);

// 開始監聽  element為單個dom元素,多個元素都需要監聽 需要多次調用  同一個觀察者對象中配置監聽多個目标元素。
obj.observe(element);

// 停止監聽
obj.unobserve(element);

// 銷毀監聽
obj.disconnect();

// 傳回所有觀察目标的對象數組 每個對象包含目标元素與根每次的相交資訊。      

 

callback回調函數中,參數是每一個監聽的對象      
var obj = new IntersectionObserver(changes => {
      //changes是一個數組,數組中每個元素都是監聽的對象,每個對象包含很多資訊,提取出需要的進行判斷
      changes.forEach(item => {
        let {
          isIntersecting,
          target
        } = item;
        if (isIntersecting) {
          lazyImgView(target);
//滿足條件後 停止監聽
          obj.unobserve(target);
        }
      })
    }, options);      
每個對象包含8個屬性      
利用IntersectionObserver完成懶加載、加載更多
time:可見性發生變化的時間,是一個高精度時間戳,機關為毫秒
target:被觀察的目标元素,是一個 DOM 節點對象
rootBounds:根元素的矩形區域的資訊,getBoundingClientRect()方法的傳回值,如果沒有根元素(即直接相對于視口滾動),則傳回null
boundingClientRect:目标元素的矩形區域的資訊
intersectionRect:目标元素與視口(或根元素)的交叉區域的資訊
intersectionRatio:目标元素的可見比例,即intersectionRect占boundingClientRect的比例,完全可見時為1,完全不可見時小于等于0
isIntersecting是用來判斷元素是否符合options中的可見條件      

例如:

var options = {
      threshold: [0.7]
    }
    var obj = new IntersectionObserver(changes => {
      changes.forEach(item => {
        console.log(item)
        let {
          isIntersecting,
          target
        } = item;
//target 出現70%,isIntersecting為true
        if (isIntersecting) {
          lazyImgView(target);
          obj.unobserve(target);
        }
      })
    }, options);      

接着看一下options

這個 options 配置對象,有三個屬性 threshold,root,rootMargin

  threshold,他是一個數組

: [0, 0.25, 0.5, 0.75, 1]      

      這個表示目标元素 0%、25%、50%、75%、100% 可見時,會觸發回調函數

​  root,​

​​監聽元素的祖先元素​

​Element​

​對象,其邊界盒将被視作視口。

    目标在根的可見區域的的任何不可見部分都會被視為不可見。

  ​

​rootMargin.​

​一個在計算交叉值時添加至根的邊界盒中的一組偏移量

    可以有效的縮小或擴大根的判定範圍進而滿足計算需要。文法大緻和CSS 中的​

​margin​

​ 屬性等同,預設值是"0px 0px 0px 0px" (字元串) 該屬性還沒有正式使用

    在交叉檢測開始之前,由​

​rootMargin規定的矩形的每一邊都會被添加至​

​​

​root​

​元素的邊框盒的相應邊。例如,可以讓你向外調整邊界,使得目标元素被認為是100%可見的,即使此元素得一部分長或寬被裁剪,或者在邊緣過于靠近根邊框盒邊界的情況下,将目标視為部分隐藏。

    ​​相關介紹​​

浏覽器相容:

利用IntersectionObserver完成懶加載、加載更多

html:

利用IntersectionObserver完成懶加載、加載更多

效果:

(為了能夠清晰的分辨 加載更多的出現,每次請求的資料和新資料之間有一個空,是以能夠看到有一個空缺)

利用IntersectionObserver完成懶加載、加載更多

完整代碼:

var imgListMoudule = (function () {
  // 擷取操作得dom元素
  var container = $('#imgList'),
    page = 1,
    supportNumber = 0;
  // 擷取資料 分頁加載
  var queryData = (page) => {
   //假資料
    data = [{
      imgurl: "img/img.png",
      id: "1"
    }, {
      imgurl: "img/img.png",
      id: "2"
    }, {
      imgurl: "img/img.png",
      id: "3"
    }, {
      imgurl: "img/img.png",
      id: "4"
    }, {
      imgurl: "img/img.png",
      id: "5"
    }, {
      imgurl: "img/img.png",
      id: "6"
    }, {
      imgurl: "img/img.png",
      id: "7"
    }, {
      imgurl: "img/img.png",
      id: "8"
    }, {
      imgurl: "img/img.png",
      id: "9"
    }]

    bindHtml(data);
    // $.ajax({
    //   type: "POST",
    //   url: "",
    //   data: "",
    //   async: true,
    //   success: function (data) {
    //     data = [{
    //       imgurl: "",
    //       id: "1"
    //     }]
         if (page > 3) {
          // 資料沒有了 将加載更多的元素去掉 不會再觸發加載更多
          $('.load_more').hide();
        }
        bindHtml(data);
        ++page;  
    //   },
    //   error: function () {
    //     console.log("送出失敗!");
    //   }
    // });
  }
  // 頁面渲染
  var bindHtml = data => {
    var result = [];
    for (var i = 0; i < data.length; i += 2) {
      result.push(data.slice(i, i + 2));
    }
    var str = "";
    result.forEach(itemArray => {
      str += ` <div class="img_list_box" lazyload>`;
      itemArray.forEach(item => {
        str += `<a href="javascript:void(0)" class="img_item" data-id="${item.id}">
                            <div class="img_box" data-id="${item.id}">
                                <img src="" alt="" class="index_img" data-realUrl="${item.imgurl}">
                            </div>
                            <div class="img_text">
                                <div class="look_box img_text_item">
                                    <span>111</span>
                                    <img src="img/look_icon.png" alt="">
                                </div>
                                <div class="zan_box img_text_item">
                                    <span>111</span>
                                    <img src="img/zan_icon.png" alt="" class="zan_btn">
                                </div>
                            </div>
                        </a>`;
      })
      str += ` </div>`;
    })
    container.append(str);

  }
  // 懶加載監聽
  var lazyLoad = () => {
    var options = {
      threshold: [0.7]
    }
    var obj = new IntersectionObserver(changes => {
      changes.forEach(item => {
        let {
          isIntersecting,
          target
        } = item;
        if (isIntersecting) {
          lazyImgView(target);
          obj.unobserve(target);
        }
      })
    }, options);
    // 擷取需要懶加載的元素  隻擷取設定了lazyload屬性得元素
    var imgBox = $('.img_list_box[lazyload]');
    [].forEach.call(imgBox, lazyImg => {

      obj.observe(lazyImg);
    })
  }
  // 懶加載渲染
  var lazyImgView = item => {
    var imgs = $(item).find('.img_box img');
    // 防止之前資料重新渲染,移除掉已經監聽得
    item.removeAttribute('lazyload');
    [].forEach.call(imgs, img => {
      img.src = img.getAttribute('data-realUrl');

      img.onload = () => {
        img.style.opacity = 1;
      }
    })
  }
  // 加載更多
  var loadMore = () => {
    var options = {
      threshold: [0.5]
    }
    var loadMoreDom = document.getElementsByClassName('load_more')[0];
    if (!loadMoreDom) {
      return false;
    }
    var obj = new IntersectionObserver(changes => {

      if (changes[0].isIntersecting) {
        queryData(page);
        lazyLoad();
      }
    }, options);

    obj.observe(loadMoreDom);
  }

 
  return {
    async init() {
      queryData(page);
      lazyLoad();
      loadMore();

    }
  }

})();
imgListMoudule.init();      

  

  

  參考

  ​​IntersectionObserver API 使用教程​​

  ​​intersectionObserver WebApiI​​