cleverMountain / jsBasic

Related js knowledge learning
1 stars 0 forks source link

12.IntersectionObserver、getBoundingClientRect与图片懒加载 #12

Open cleverMountain opened 1 year ago

cleverMountain commented 1 year ago

1.提供了一种异步观察目标元素与其祖先元素或顶级文档视口(viewport)交叉状态的方法。其祖先元素或视口被称为根(root) 语法

var observer = new IntersectionObserver(callback[, options]);

callback:当元素可见比例超过指定阈值后,会调用一个回调函数,此回调函数接受两个参数(entries:目标元素集合, observer:实例) options:配置 observer 实例的对象。如果options未指定,observer 实例默认使用文档视口作为 root,并且没有 margin,阈值为 0%。接收三个参数(root:祖先元素,rootMargin,threshold:目标元素与父元素交叉值)

        let options = {
          root: null, // 默认视口
          rootMargin: '0px',
          threshold: 0 // 完全可见触发回调函数
        }
        // 容器元素又称为 intersection root,或 root element。这个既可以是 target 元素祖先元素也可以是指定 null 则使用浏览器视口做为容器 (root)。
        let callback = (entries, observer) => {
          // entries观察者对象 observer观察者 祖先元素
          console.log(entries)
          entries.forEach(entry => {
            console.log(entry, observer)
            // Each entry describes an intersection change for one observed target element:
            // entry.boundingClientRect
            // entry.intersectionRatio
            // entry.intersectionRect
            // entry.isIntersecting
            // entry.rootBounds
            // entry.target
            // entry.time
          });
        };
        let observer = new IntersectionObserver(callback, options);
        let target = document.querySelector('#listItem');
        observer.observe(target);
cleverMountain commented 1 year ago

2.实现图片懒加载

     <head>
    <style>
      .img {
        width: 200px;
        height: 200px;
        background-color: gray;
        margin-bottom: 20px;
      }

      .pic {
        width: 100%;
        height: 100%;
      }
    </style>
  </head>
  <!-- 图片来自网络,侵删。 -->
  <body>
    <div class="container">
      <div class="img">
        <!-- 注意我们并没有为它引入真实的src -->
        <img class="pic" alt="加载中"
          data-src="https://tse1-mm.cn.bing.net/th/id/OIP.8OrEFn_rKe82kqAWFjTuMwHaEo?pid=Api&rs=1" />
      </div>
      <div class="img">
        <img class="pic" alt="加载中"
          data-src="https://ssl.tzoo-img.com/images/tzoo.94911.0.910013.seoul-nami.jpg?width=1080" />
      </div>
      <div class="img">
        <img class="pic" alt="加载中"
          data-src="https://tse4-mm.cn.bing.net/th/id/OIP.ZitgAuABnwkrGn4lid2ZmQHaEK?pid=Api&rs=1" />
      </div>
      <div class="img">
        <img class="pic" alt="加载中" data-src="http://pic34.photophoto.cn/20150315/0034034862056002_b.jpg" />
      </div>
      <div class="img">
        <img class="pic" alt="加载中"
          data-src="http://img.mp.sohu.com/upload/20170724/32d4409f34194b029ed287abf1c99b70_th.png" />
      </div>
      <div class="img">
        <img class="pic" alt="加载中" data-src="https://pic6.wed114.cn/20180829/2018082910075991913520.jpg" />
      </div>
      <div class="img">
        <img class="pic" alt="加载中"
          data-src="https://tse4-mm.cn.bing.net/th/id/OIP.PZdPKj3sXEX2jLrepx3MUwHaEo?pid=Api&rs=1" />
      </div>
      <div class="img">
        <img class="pic" alt="加载中" data-src="https://pic6.wed114.cn/20180829/2018082910075831439349.jpg" />
      </div>
      <div class="img">
        <img class="pic" alt="加载中" data-src="https://pic6.wed114.cn/20180829/2018082910075468043336.jpg" />
      </div>
      <div class="img">
        <img class="pic" alt="加载中"
          data-src="https://tse2-mm.cn.bing.net/th/id/OIP.CRYz5Bv4vylsMh83G4CsLgHaFj?pid=Api&rs=1" />
      </div>
    </div>
    <script>
      let options = {
        root: null,
        rootMargin: '0px',
        threshold: 1 // 完整出现触发
      }
      let callback = (entries, observer) => {
        debugger
        entries.forEach(entry => {
          // 元素像素点出现50%时触发
          console.log(entry.intersectionRatio)
          if (entry.intersectionRatio > 0.5) {

            entry.target.setAttribute('src', entry.target.getAttribute('data-src'))

          }
        })
      }
      let observer = new IntersectionObserver(callback, options);
      let target = document.getElementsByClassName('pic')
      // 给每一个图片添加观测者
      Array.prototype.forEach.call(target, (item) => {
        observer.observe(item)
      })
    </script>
  </body>
cleverMountain commented 1 year ago

3.其它测试

  <!DOCTYPE html>
  <html lang="en">

  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
      #box {
        background-color: rgba(40, 40, 190, 255);
        border: 4px solid rgb(20, 20, 120);
        transition: background-color 1s, border 1s;
        width: 350px;
        height: 350px;
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 20px;
      }

      .vertical {
        color: white;
        font: 32px "Arial";
      }

      .extra {
        width: 350px;
        height: 350px;
        margin-top: 10px;
        border: 4px solid rgb(20, 20, 120);
        text-align: center;
        padding: 20px;
      }

      .li {
        margin-top: 500px;
      }
    </style>
  </head>

  <body>
    <div class="li">da</div>
    <div class="li">da</div>
    <divc lass="li">da</div>
      <div id="box">
        <div class="vertical">
          Welcome to <strong>The Box!</strong>
        </div>
      </div>
      <script>
        function createObserver() {
          let observer;
          let options = {
            root: null,
            rootMargin: "0px",
            // 默认值是 0 (意味着只要有一个 target 像素出现在 root 元素中,回调函数将会被执行)。
            // 该值为 1.0 含义是当 target 完全出现在 root 元素中时候 回调才会被执行。
            //  target 在 root 元素中中的可见性每超过 0.1 或者减少 0.1 的时候都通知一次
            threshold: buildThresholdList() // [0,0.1, 0.2]数组,有几个触发几次 0-0.1触发一次 0.1-0.2再触发一次
          };
          observer = new IntersectionObserver(handleIntersect, options);
          observer.observe(boxElement);
        }
        function buildThresholdList() {
          let thresholds = [];
          let numSteps = 20;
          // thresholds.push(0);
          for (let i = 1; i <= numSteps; i++) {
            let ratio = i / numSteps;
            thresholds.push(ratio);
          }
          // 触发次数
          console.log(thresholds)
          return thresholds;
        }
        function handleIntersect(entries, observer) {
          entries.forEach((entry) => {
            // intersectionRatio: 当前元素占比视口多少
            // target: 目标元素
            if (entry.intersectionRatio > prevRatio) {
              console.log(entry)
              // 透明度0-1
              entry.target.style.backgroundColor = increasingColor.replace("ratio", entry.intersectionRatio);
            } else {
              entry.target.style.backgroundColor = decreasingColor.replace("ratio", entry.intersectionRatio);
            }
            prevRatio = entry.intersectionRatio;
            console.log(prevRatio)
          });
        }
        const numSteps = 20.0;
        let boxElement;
        let prevRatio = 0.0;
        // 添加透明度
        let increasingColor = "rgba(40, 40, 190, ratio)";
        let decreasingColor = "rgba(190, 40, 40, ratio)";
        // Set things up
        window.addEventListener("load", (event) => {
          boxElement = document.querySelector("#box");
          createObserver();
        }, false);

      </script>
  </body>

  </html>
cleverMountain commented 1 year ago

4.图片懒加载传统方式实现

<head>
  <style>
    .img {
      width: 200px;
      height: 200px;
      background-color: gray;
      margin-bottom: 20px;
    }

    .pic {
      width: 100%;
      height: 100%;
    }
  </style>
</head>
<!-- 图片来自网络,侵删。 -->

<body>
  <div class="container">
    <div class="img">
      <!-- 注意我们并没有为它引入真实的src -->
      <img class="pic" alt="加载中"
        data-src="https://tse1-mm.cn.bing.net/th/id/OIP.8OrEFn_rKe82kqAWFjTuMwHaEo?pid=Api&rs=1" />
    </div>
    <div class="img">
      <img class="pic" alt="加载中"
        data-src="https://ssl.tzoo-img.com/images/tzoo.94911.0.910013.seoul-nami.jpg?width=1080" />
    </div>
    <div class="img">
      <img class="pic" alt="加载中"
        data-src="https://tse4-mm.cn.bing.net/th/id/OIP.ZitgAuABnwkrGn4lid2ZmQHaEK?pid=Api&rs=1" />
    </div>
    <div class="img">
      <img class="pic" alt="加载中" data-src="http://pic34.photophoto.cn/20150315/0034034862056002_b.jpg" />
    </div>
    <div class="img">
      <img class="pic" alt="加载中"
        data-src="http://img.mp.sohu.com/upload/20170724/32d4409f34194b029ed287abf1c99b70_th.png" />
    </div>
    <div class="img">
      <img class="pic" alt="加载中" data-src="https://pic6.wed114.cn/20180829/2018082910075991913520.jpg" />
    </div>
    <div class="img">
      <img class="pic" alt="加载中"
        data-src="https://tse4-mm.cn.bing.net/th/id/OIP.PZdPKj3sXEX2jLrepx3MUwHaEo?pid=Api&rs=1" />
    </div>
    <div class="img">
      <img class="pic" alt="加载中" data-src="https://pic6.wed114.cn/20180829/2018082910075831439349.jpg" />
    </div>
    <div class="img">
      <img class="pic" alt="加载中" data-src="https://pic6.wed114.cn/20180829/2018082910075468043336.jpg" />
    </div>
    <div class="img">
      <img class="pic" alt="加载中"
        data-src="https://tse2-mm.cn.bing.net/th/id/OIP.CRYz5Bv4vylsMh83G4CsLgHaFj?pid=Api&rs=1" />
    </div>
  </div>
  <script>
    let images = document.getElementsByClassName('pic')
    // 获取视口高度
    const viewportHeight = document.body.clientHeight || document.documentElement.clientHeight

    const show = () => {
      console.log(1)
      Array.prototype.forEach.call(images, (item) => {
        console.log(item.getBoundingClientRect().top)
        if (viewportHeight - item.getBoundingClientRect().top > 0) {
          item.src = item.getAttribute('data-src')
        }
      })
    }

    function debounce(func, wait) {
      let timer = null
      return function () {
        let args = arguments
        timer && clearTimeout(timer)
        timer = setTimeout(() => {
          func.call(this, ...args)
        }, wait)
      }
    }
    window.onload = show()

    window.addEventListener('mousewheel', debounce(show, 600), false);

  </script>
</body>
cleverMountain commented 1 year ago

5.getBoundingClientRect,法返回一个 [DOMRect] 对象,其提供了元素的大小及其相对于[视口]的位置。top:距离视口的高度 left botom right