我如何知道IntersectionObserver滚动方向?

Jos*_*des 26 html javascript 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);
Run Code Online (Sandbox Code Playgroud)
#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;
}
Run Code Online (Sandbox Code Playgroud)
<div id="element">Observed</div>
Run Code Online (Sandbox Code Playgroud)

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

Mat*_*vic 24

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

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,
      },
    );
Run Code Online (Sandbox Code Playgroud)


Jas*_*son 13

我不知道处理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)
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
<div class="observer__target"></div>
<span class="observer__state"></span>
Run Code Online (Sandbox Code Playgroud)


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

  • 这只会创建一个包含20个空插槽的新数组,然后将每个插槽填充零,然后使用map()将索引除以20。结果为0.05,0.1,0.15,0.2,依此类推,直到达到索引20,等于1.0 [20/20]。这些是交叉点阈值点。 (3认同)
  • 您的“if/else”语句中的条件设置不正确。您需要将“isIntersecting”变量作为条件添加到“向上滚动离开”“if”语句中,并且需要向其添加“!”逻辑运算符。就像这样:“if (currentRatio &lt; previousRatio &amp;&amp; !isIntersecting)”。您的第一个“else if”语句中有“isIntersecting”变量,当您尝试向上/离开时,该变量会破坏功能。该变量不需要处于更高的条件。不确定这是错误还是疏忽。 (3认同)

小智 8

比较boundingClientRectrootBoundsentry,你可以很容易地知道,如果目标是高于或低于视.

在期间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;
    });
}
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助.

  • 在一些罕见的情况下,这可能无法工作,例如,当在没有动画的情况下从目标位于视口下方的滚动位置直接滚动到视口上方的滚动位置时。由于目标在两种情况下都不相交,因此浏览器不会触发回调。 (2认同)