javascript 我如何知道 IntersectionObserver 滚动方向?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/46478202/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 06:41:34  来源:igfitidea点击:

How do I know the IntersectionObserver scroll direction?

javascripthtmlcssintersection-observer

提问by Jose Paredes

So, how do I know the scroll direction when the event it's triggered?

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

In the returned object the closest possibility I see is interacting with the boundingClientRectkind of saving the last scroll position but I don't know if handling boundingClientRectwill end up on performance issues.

在返回的对象中,我看到的最接近的可能性是与boundingClientRect保存最后一个滚动位置的那种交互,但我不知道处理boundingClientRect是否会导致性能问题。

Is it possible to use the intersection event to figure out the scroll direction (up / down)?

是否可以使用相交事件来确定滚动方向(向上/向下)?

I have added this basic snippet, so if someone can help me.
I will be very thankful.

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

Here is the snippet:

这是片段:

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>

I would like to know this, so I can apply this on fixed headers menu to show/hide it

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

采纳答案by Jason

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

我不知道处理 boundingClientRect 是否会导致性能问题。

MDN states that the IntersectionObserverdoes not run on the main thread:

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"

MDN,“Intersection Observer API”

We can compute the scrolling direction by saving the value of IntersectionObserverEntry.boundingClientRect.yand compare that to the previous value.

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

Run the following snippet for an example:

运行以下代码段作为示例:

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>



If the thresholdArrayhelper function might confuse you, it builds an array ranging from 0.0to 1.0by the given amount of steps. Passing 5will return [0.0, 0.2, 0.4, 0.6, 0.8, 1.0].

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

回答by thierrymichel

Comparing boundingClientRectand rootBoundsfrom entry, you can easily know if the target is above or below the viewport.

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

During callback(), you check isAbove/isBelow then, at the end, you store it into wasAbove/wasBelow. Next time, if the target comes in viewport (for example), you can check if it was above or below. So you know if it comes from top or bottom.

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

You can try something like this:

你可以尝试这样的事情:

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;
    });
}

Hope this helps.

希望这可以帮助。

回答by stefan judis

:)

:)

I don't think this is possible with a single threshold value. You could try to watch out for the intersectionRatiowhich in most of the cases is something below 1when the container leaves the viewport (because the intersection observer fires async). I'm pretty sure that it could be 1too though if the browser catches up quickly enough. (I didn't test this :D )

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

But what you maybe could do is observe two thresholds by using several values. :)

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

threshold: [0.9, 1.0]

If you get an event for the 0.9first it's clear that the container enters the viewport...

如果您获得第一个事件,0.9很明显容器进入视口...

Hope this helps. :)

希望这可以帮助。:)

回答by bob

My requirement was:

我的要求是:

  • do nothing on scroll-up
  • on scroll-down, decide if an element started to hide from screen top
  • 向上滚动时什么都不做
  • 向下滚动时,确定元素是否开始从屏幕顶部隐藏

I needed to see a few information provided from IntersectionObserverEntry:

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

  • intersectionRatio (should be decreasing from 1.0)
  • boundingClientRect.bottom
  • boundingClientRect.height
  • 交叉比(应该从 1.0 开始减小)
  • boundingClientRect.bottom
  • boundingClientRect.height

So the callback ended up look like:

所以回调最终看起来像:

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] });

See my postfor complete codes.

有关完整代码,请参阅我的帖子