我如何知道IntersectionObserver滚动方向?

时间:2017-09-28 20:58:00

标签: javascript html css intersection-observer

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

在返回的对象中,我看到的最接近的可能性是与保存最后滚动位置的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>

我想知道这一点,所以我可以在固定标题菜单上应用它来显示/隐藏它

5 个答案:

答案 0 :(得分:8)

  

我不知道处理boundingClientRect是否最终会遇到性能问题。

MDN指出IntersectionObserver不在主线程上运行:

  

这样,站点不再需要在主线程上执行任何操作来监视这种元素交集,并且浏览器可以自由地优化交集的管理。

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将返回[0.0, 0.2, 0.4, 0.6, 0.8, 1.0]

答案 1 :(得分:6)

比较来自boundingClientRect的{​​{1}}和rootBounds,您可以轻松了解目标是在视口之上还是之下。

entry期间,检查isAbove / isBelow,最后将它存储到wasAbove / wasBelow。 下一次,如果目标进入视口(例如),您可以检查它是在上面还是下面。所以你知道它是来自顶部还是底部。

您可以尝试这样的事情:

callback()

希望这有帮助。

答案 2 :(得分:2)

:)

我认为单个阈值不可能实现这一点。您可以尝试注意intersectionRatio,当容器离开视口时,大多数情况下1低于1(因为交叉点观察者触发异步)。如果浏览器足够迅速赶上,我很确定它也可能是threshold: [0.9, 1.0] 。 (我没试过这个:D)

但你可能做的是通过使用几个值来观察两个阈值。 :)

0.9

如果您首先获得{{1}}的事件,则表明容器已进入视口......

希望这会有所帮助。 :)

答案 3 :(得分:1)

我的要求是:

  • 在向上滚动
  • 时不做任何事情
  • 在向下滚动时,确定元素是否开始从屏幕顶部隐藏

我需要查看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

答案 4 :(得分: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,
      },
    );