Intersection Observer 之 rootMargin

2,745 阅读5分钟

主要详细解析rootMargin,

Intersection Observer

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

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

API 提供了一种异步检测目标元素与祖先元素或 viewport 相交情况变化的方法。 Intersection Observer API 会注册一个回调函数,每当被监视的元素进入或者退出另外一个元素时(或者 viewport ),或者两个元素的相交部分大小发生变化时,该回调方法会被触发执行。这样,我们网站的主线程不需要再为了监听元素相交而辛苦劳作,浏览器会自行优化元素相交管理。

isIntersecting 和 intersectionRatio

当你想要在 target 在 root 元素中中的可见性每超过 25%或者减少 25%的时候都通知一次。你可以在创建 observer 的时候指定 thresholds 属性值为[0, 0.25, 0.5, 0.75, 1],你可以通过检测在每次交集发生变化的时候的都会传递回调函数的参数"IntersectionObserverEntry.isIntersecting"的属性值来判断 target 元素在 root 元素中的可见性是否发生变化。如果 isIntersecting 是 true,target 元素的至少已经达到 thresholds 属性值当中规定的其中一个阈值,如果是 false,target 元素不在给定的阈值范围内可见。


new IntersectionObserver(
    entries => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          // 可见
        } else {

      })
    },
    {
      threshold: exposureRate || 0.8, // 可视区域 >= 80% 视为曝光
    }
)

rootMargin

developer.mozilla.org/zh-CN/docs/…

多方的定义,但是依然不十分具体

IntersectionObserver 接口的只读属性rootMargin是与CSS属性margin语法相似的字符串(string)对象. 在交叉检测开始之前,由rootMargin规定的矩形的每一边都会被添加至root元素的边框盒(bounding box (en-US))的相应边。例如,这可以让你向外调整边界,使得目标元素被认为是100%可见的,即使此元素得一部分长或宽被裁剪,或者在边缘过于靠近根边框盒边界的情况下,将目标视为部分隐藏。

用作描述intersection root 元素边界的矩形可以使用root margin来调整矩形大小,即rootMargin属性,在我们创建IntersectionObserver对象的时候使用。rootMargin的属性值将会做为margin偏移值添加到intersection root 元素的对应的margin位置,并最终形成root 元素的矩形边界。

根(root)元素的外边距。类似于 CSS 中的 margin 属性,比如 "10px 20px 30px 40px" (top, right, bottom, left)。如果有指定root参数,则rootMargin也可以使用百分比来取值。该属性值是用作root元素和target发生交集时候的计算交集的区域范围,使用该属性可以控制root元素每一边的收缩或者扩张。默认值为0。

rootMargin自我分析

rootMargin的top为负值时候, 当target元素是向上滚动消失,是target与root的上边界intersection前扩大的root的rootBounds,常见于页面顶部距离顶部一定为主要pin住某个target

或者也可以这样理解,root元素,多了一个margin属性,如果没有这个margin属性,ele元素只有与root元素开始交叉时才会触发可视性的变化,而这个rootMargin属性的话,就是当ele元素与root元素的外边距交叉时,就会触发ele元素的可视性变化。

那么rootMagin的好处在哪里呢?可以理解为,懒加载的预加载,比如图片的懒加载,不是等图片元素进入可视区域之后,才去加载,而是检测到,离进入可视区域,在一定范围内部的时候,就提前加载(叫做预加载吧),这样,当真的进入视图之内,说不定已经加载好了,可以大大的提升整个产品的体验。

具体事例:target本身的正数值是增大视区的检测区域,负值反而是减小。具体表现如下

当rootMargin的top为正数,只看上边界

  • 其实只要记住其中一个就行,另一个反向就知道了;个人建议只看向上消失场景更容易理解

  • 当内容向上滚动target消失时,在与root的上边界触发检测前,target的延后 top px被检测消失

  • 当内容继续向下滚动target出现时,在与root的上边界触发检测前,target的提前 top px被检测消失

rootMargin当top为负数,只看上边界

  • 当内容向上滚动target消失时,在与root的上边界触发检测前,target的提前 top px被检测消失

  • 当内容向下滚动target出现时,在与root的上边界触发检测前,target的延后 top px被检测消失

rootMargin当bottom为正数,只看下边界

  • 当内容向上滚动target出现时,在与root的下边界触发检测前,target的提前bottom px被检测

  • 当内容向下滚动target消失时,在与root的下边界触发检测前,target的延后 top px被检测消失

rootMargin当bottom为负数,只看下边界

  • 当内容向上滚动到target出现时,在与root的下边界触发检测前,target的延后bottom px被检测,

  • 当内容向下滚动到target消失时,在与root的下边界触发检测前,target的提前bottom px被检测,

rootMargin总结

总结就是。margin是target与对应边界intersection产生影响 ,不影响对另一个边界的影响,

target本身的正数值是增大视区的检测区域,负值反而是减小

  • top为正数可以理解为与上边界交互过程中:当前检测区域上边界变大,所以target消失就延后;
  • 为负数相当于当前检测区域上边界变小,所以target消失就提前;
  • bottom为正数可以理解为与下边界交互过程中:当前检测区域上边界变大,所以target出现就提前;
  • 为负数相当于当前检测区域下边界变小,所以target出现就延后;

可参考demo

<html>
<style>
  * {
    padding: 0;
    margin: 0;
  }

  .pad {
    height: 1200px;
  }

  #target {
    background: rgb(237, 28, 36);
    height: 100px;
    outline: 100px solid rgba(0, 0, 0, 0.2);
  }

  #info {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-size: 2rem;
  }
</style>

<body>
  <div class="pad"></div>
  <div id="target"></div>
  <div class="pad"></div>
  <span id="info">isIntersecting = true</span>
  <script>
    debugger
    var io = new IntersectionObserver(
      function (entries) {
        document.getElementById("info").innerHTML = entries[0].isIntersecting
          ? "isIntersecting = true"
          : "isIntersecting = false";
      },
      {
        rootMargin: "0px 0px 100px 0px"
      }
    );
    io.observe(document.getElementById("target"));
  </script>
</body>

</html>