55

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

在返回的对象中,我看到的最接近的可能性是与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>

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

4

5 回答 5

50

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

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

这样,网站不再需要在主线程上做任何事情来监视这种元素交集,并且浏览器可以自由地优化它认为合适的交集管理。

MDN,“交叉口观察者 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]

于 2018-08-23T00:24:50.470 回答
13

比较boundingClientRectrootBoundsfrom 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;
    });
}

希望这可以帮助。

于 2017-11-05T02:18:52.227 回答
8

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

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,
      },
    );
于 2020-09-09T21:51:11.910 回答
2

:)

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

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

threshold: [0.9, 1.0]

如果您首先收到一个事件,则0.9很明显容器进入了视口......

希望这可以帮助。:)

于 2017-09-29T07:45:57.313 回答
2

我的要求是:

  • 向上滚动时不执行任何操作
  • 在向下滚动时,确定元素是否开始从屏幕顶部隐藏

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

  • 交叉比率(应该从 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] });

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

于 2017-11-28T21:58:38.280 回答