IntersectionObserver 监听元素是不是出现或离开可视区域

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IntersectionObserver 监听元素是不是出现或离开可视区域相关的知识,希望对你有一定的参考价值。

参考技术A IntersectionObserver 这个 API,它是异步的,不会卡主线程,相比以前监听滚动事件计算可视区域的元素,更优秀,但是不兼容 IE,好在官方提供了 polyfill 来解决这个问题。详细地址:

https://github.com/w3c/IntersectionObserver/blob/main/polyfill/intersection-observer.js

// 下面是一个手机端vue项目的应用
// 用来懒加载图片,列表一次拉20条数据,但是数据中有大图片,
// 一次加载20张大图片很慢,所以把<li>中img设置成隐藏,
// 然后循环的时候,通过序号curTargetIndex来添加class,把隐藏的图片样式设置成block(这步其实可以不写,直接用v-if判断img是否出现)
// 实现图片懒加载,光是显示隐藏还不够,这个只是影响浏览器渲染
// 必须用用v-if判断是否插入img标签, 条件和通过序号curTargetIndex来添加class是一样的
// <img v-if="v-if="(index <= (activeIndex + 3) && index >= (activeIndex - 2))"" />

IntersectionObserver

创建对象

var io = new IntersectionObserver(callback, option);

IntersectionObserver是浏览器原生提供的构造函数,接受两个参数:callback是可见性变化时的回调函数,option是配置对象(该参数可选)。

构造函数的返回值是一个观察器实例。实例的observe方法可以指定观察哪个 DOM 节点。

// 开始观察,参数是观察对象元素
io.observe(document.getElementById(example));

// 停止观察
io.unobserve(element);

// 关闭观察器
io.disconnect();

如果要观察多个节点,就要多次调用这个方法。

io.observe(elementA);
io.observe(elementB);

 

callback 参数

目标元素的可见性变化时,就会调用观察器的回调函数callbackcallback一般会触发两次。一次是目标元素刚刚进入视口(开始可见),另一次是完全离开视口(开始不可见)。callback函数的参数(entries)是一个数组,每个成员都是一个IntersectionObserverEntry对象

const intersectionObserver = new IntersectionObserver((entries) => {
    console.log(entries);# entries 返回数组
for (entry of entries)
{
if (entry.intersectionRatio > 0)
   {
    addAnimationClass(entry.target, animationClass);
}
else {
     console.log(animationClass); removeAnimationClass(entry.target, animationClass);
}
}
}

IntersectionObserverEntry对象一共有六个属性

技术分享图片

  • boundingClientRect:目标元素的矩形区域的信息
  • intersectionRatio:目标元素的可见比例,即intersectionRect占boundingClientRect的比例,完全可见时为1,完全不可见时小于等于0
  • intersectionRect:目标元素与视口(或根元素)的交叉区域的信息
  • rootBounds:根元素的矩形区域的信息,getBoundingClientRect()方法的返回值,如果没有根元素(即直接相对于视口滚动),则返回null
  • target:被观察的目标元素,是一个 DOM 节点对象
  • time:可见性发生变化的时间,是一个高精度时间戳,单位为毫秒

Option 对象

IntersectionObserver构造函数的第二个参数是一个配置对象。它可以设置以下属性。

6.1 threshold 属性

threshold属性决定了什么时候触发回调函数。它是一个数组,每个成员都是一个门槛值,默认为[0],即交叉比例(intersectionRatio)达到0时触发回调函数。

new IntersectionObserver(
  entries => {/* ... */}, 
  {
    threshold: [0, 0.25, 0.5, 0.75, 1]
  }
);

用户可以自定义这个数组。比如,[0, 0.25, 0.5, 0.75, 1]就表示当目标元素 0%、25%、50%、75%、100% 可见时,会触发回调函数。

6.2 root 属性,rootMargin 属性

很多时候,目标元素不仅会随着窗口滚动,还会在容器里面滚动(比如在iframe窗口里滚动)。容器内滚动也会影响目标元素的可见性,IntersectionObserver API 支持容器内滚动。root属性指定目标元素所在的容器节点(即根元素)。注意,容器元素必须是目标元素的祖先节点。

var option = { 
  root: document.querySelector(.container),
  rootMargin: "500px 0px" 
};

var observer = new IntersectionObserver(
  callback,
   option
);

上面代码中,除了root属性,还有rootMargin属性。后者定义根元素的margin,用来扩展或缩小rootBounds这个矩形的大小,从而影响intersectionRect交叉区域的大小。它使用CSS的定义方法,比如10px 20px 30px 40px,表示 top、right、bottom 和 left 四个方向的值。

这样设置以后,不管是窗口滚动或者容器内滚动,只要目标元素可见性变化,都会触发观察器。

 

惰性加载案例

技术分享图片
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <!-- <script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=es6"></script> -->
    <title>IntersectionObserver</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        div {
            margin: 0 auto;
            max-width: 100%;
            width: 600px;
        }

        #top {
            height: 1200px;
            background-color: #aaaaaa;
        }

        #middle {
            margin-top: 200px;
            opacity: 0;
            height: 400px;
            background-color: #000000;
        }

        #bottom {
            height: 300px;
            background-color: #333;
        }

        #middle.move,
        #bottom.move {
            animation: movefromleft 2s;
            animation-fill-mode: forwards;
        }

        @keyframes movefromleft {
            from {
                opacity: 0;
                transform: translateX(-300px);
            }
            to {
                opacity: 1;
                transform: translateX(0px);
            }
        }
    </style>
</head>

<body>
    <div id="top"></div>
    <div id="middle"></div>
    <div id="bottom"></div>
</body>
<script>
    window.onload = (event) => {
        const middle = document.getElementById(middle),
            bottom = document.getElementById(bottom);
        const animationClass = move;
        // 创建监听
        const intersectionObserver = new IntersectionObserver((entries) => {
            console.log(entries);
            for (entry of entries) {
                if (entry.intersectionRatio > 0) {
                    addAnimationClass(entry.target, animationClass);
                } else {
                    console.log(animationClass);
                    removeAnimationClass(entry.target, animationClass);
                }
            }
        });
        // 添加动画class的操作
        function addAnimationClass(elem, animationClass) {
            elem.className.includes(animationClass) ? 1 : elem.className = elem.className +   + animationClass;
        }
        // 移除动画class的操作
        function removeAnimationClass(elem, animationClass) {
            elem.className.includes(animationClass) ? elem.className = elem.className.replace(animationClass, ‘‘) : 1;
            console.log(elem.className);
        }
        // 开启监听
        intersectionObserver.observe(middle);
        intersectionObserver.observe(bottom);
    }

</script>

</html>
View Code

 

注意:

IntersectionObserver API 是异步的,不随着目标元素的滚动同步触发。

规格写明,IntersectionObserver的实现,应该采用requestIdleCallback(),即只有线程空闲下来,才会执行观察器。这意味着,这个观察器的优先级非常低,只在其他任务执行完,浏览器有了空闲才会执行。

文章引用: IntersectionObserver API 使用教程

      Intersection Observer











以上是关于IntersectionObserver 监听元素是不是出现或离开可视区域的主要内容,如果未能解决你的问题,请参考以下文章

「前端进阶」高性能渲染十万条数据(虚拟DOM解决方案)

IntersectionObserver API

使用IntersectionObserver更高效的监视某个页面元素是否进入了可见窗口

使用IntersectionObserver更高效的监视某个页面元素是否进入了可见窗口

IntersectionObserver

使用IntersectionObserver进行曝光打点