我如何知道 IntersectionObserver 滚动方向?

How do I know the IntersectionObserver scroll direction?

那么,当事件被触发时,我如何知道滚动方向?

在返回的 object 中,我看到的最接近的可能性是与 boundingClientRect 交互,保存最后的滚动位置,但我不知道处理 boundingClientRect 是否会结束关于性能问题。

是否可以使用交集事件来计算滚动方向(向上/向下)?

我已经添加了这个基本片段,所以如果有人可以帮助我。
我会很感激的。

这是片段:

var options = {
  rootMargin: '0px',
  threshold: 1.0
}

function callback(entries, observer) { 
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      console.log('entry', entry);
    }
  });
};

var elementToObserve = document.querySelector('#element');
var observer = new IntersectionObserver(callback, options);

observer.observe(elementToObserve);
#element {
  margin: 1500px auto;
  width: 150px;
  height: 150px;
  background: #ccc;
  color: white;
  font-family: sans-serif;
  font-weight: 100;
  font-size: 25px;
  text-align: center;
  line-height: 150px;
}
<div id="element">Observed</div>

我想知道这个,所以我可以在固定 headers 菜单上将其应用到 show/hide 它

:)

我不认为这是可能的一个单一的阈值。您可以尝试注意 intersectionRatio,在大多数情况下,当容器离开视口时,它低于 1(因为交集观察器触发异步)。我很确定它也可能是 1,但如果浏览器足够快地跟上的话。 (我没有测试这个 :D )

但是您可以做的是通过使用多个值来观察两个阈值。 :)

threshold: [0.9, 1.0]

如果您首先获得 0.9 的事件,则很明显容器进入了视口...

希望这对您有所帮助。 :)

比较boundingClientRectrootBounds来自entry,您可以轻松知道目标是在视口上方还是下方。

callback() 期间,您检查 isAbove/isBelow,然后在最后将其存储到 wasAbove/wasBelow。 下次,如果目标出现在视口中(例如),您可以检查它是在上方还是下方。所以你知道它是来自顶部还是底部。

您可以尝试这样的操作:

var wasAbove = false;

function callback(entries, observer) {
    entries.forEach(entry => {
        const isAbove = entry.boundingClientRect.y < entry.rootBounds.y;

        if (entry.isIntersecting) {
            if (wasAbove) {
                // Comes from top
            }
        }

        wasAbove = isAbove;
    });
}

希望这对您有所帮助。

我的要求是:

  • 向上滚动时什么也不做
  • 向下滚动时,决定元素是否开始从屏幕顶部隐藏

我需要查看 IntersectionObserverEntry 提供的一些信息:

  • intersectionRatio(应该从 1.0 开始减少)
  • boundingClientRect.bottom
  • boundingClientRect.height

所以回调最终看起来像:

intersectionObserver = new IntersectionObserver(function(entries) {
  const entry = entries[0]; // observe one element
  const currentRatio = intersectionRatio;
  const newRatio = entry.intersectionRatio;
  const boundingClientRect = entry.boundingClientRect;
  const scrollingDown = currentRatio !== undefined && 
    newRatio < currentRatio &&
    boundingClientRect.bottom < boundingClientRect.height;

  intersectionRatio = newRatio;

  if (scrollingDown) {
    // it's scrolling down and observed image started to hide.
    // so do something...
  }

  console.log(entry);
}, { threshold: [0, 0.25, 0.5, 0.75, 1] });

有关完整代码,请参阅 my post

I don't know if handling boundingClientRect will end up on performance issues.

MDN 声明 IntersectionObserver 在主线程上不 运行:

This way, sites no longer need to do anything on the main thread to watch for this kind of element intersection, and the browser is free to optimize the management of intersections as it sees fit.

MDN, "Intersection Observer API"

我们可以通过保存 IntersectionObserverEntry.boundingClientRect.y 的值并将其与之前的值进行比较来计算滚动方向。

运行 以下代码段为例:

const state = document.querySelector('.observer__state')
const target = document.querySelector('.observer__target')

const thresholdArray = steps => Array(steps + 1)
 .fill(0)
 .map((_, index) => index / steps || 0)

let previousY = 0
let previousRatio = 0

const handleIntersect = entries => {
  entries.forEach(entry => {
    const currentY = entry.boundingClientRect.y
    const currentRatio = entry.intersectionRatio
    const isIntersecting = entry.isIntersecting

    // Scrolling down/up
    if (currentY < previousY) {
      if (currentRatio > previousRatio && isIntersecting) {
        state.textContent ="Scrolling down enter"
      } else {
        state.textContent ="Scrolling down leave"
      }
    } else if (currentY > previousY && isIntersecting) {
      if (currentRatio < previousRatio) {
        state.textContent ="Scrolling up leave"
      } else {
        state.textContent ="Scrolling up enter"
      }
    }

    previousY = currentY
    previousRatio = currentRatio
  })
}

const observer = new IntersectionObserver(handleIntersect, {
  threshold: thresholdArray(20),
})

observer.observe(target)
html,
body {
  margin: 0;
}

.observer__target {
  position: relative;
  width: 100%;
  height: 350px;
  margin: 1500px 0;
  background: rebeccapurple;
}

.observer__state {
  position: fixed;
  top: 1em;
  left: 1em;
  color: #111;
  font: 400 1.125em/1.5 sans-serif;
  background: #fff;
}
<div class="observer__target"></div>
<span class="observer__state"></span>


如果 thresholdArray 辅助函数可能会让您感到困惑,它会按照给定的步数构建一个范围从 0.01.0 的数组。传递 5 将 return [0.0, 0.2, 0.4, 0.6, 0.8, 1.0]

此解决方案不使用任何外部状态,因此比跟踪其他变量的解决方案更简单:

const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.boundingClientRect.top < 0) {
          if (entry.isIntersecting) {
            // entered viewport at the top edge, hence scroll direction is up
          } else {
            // left viewport at the top edge, hence scroll direction is down
          }
        }
      },
      {
        root: rootElement,
      },
    );