当从顶部或底部滚动到视图中时,使用 Intersection Observer 向元素添加不同的 类

Use Intersection Observer to add different classes to elements when scrolled into view from top or bottom

我有一段 jQuery 代码,当元素滚动到视口中时向元素添加 css class 并在滚动时删除 class在视口之外。

到目前为止,代码是这样工作的:


到目前为止一切顺利。但我想要实现的是:

滚动到视图中:

滚出视图:

清理中:


有人在评论中建议使用 Intersection Observer API,在阅读更多相关信息后,我相信它提供了满足要求的最佳方法。

这是我的代码(以整页打开 - 预览效果不佳)。您还可以找到 the same code on jsFiddle.

function inView(opt) {
  if (opt.selector === undefined) {
    console.log('Valid selector required for inView');
    return false;
  }
  var elems = [].slice.call(document.querySelectorAll(opt.selector)),
    once = opt.once === undefined ? true : opt.once,
    offsetTop = opt.offsetTop === undefined ? 0 : opt.offsetTop,
    offsetBot = opt.offsetBot === undefined ? 0 : opt.offsetBot,
    count = elems.length,
    winHeight = 0,
    ticking = false;

  function update() {
    var i = count;
    while (i--) {
      var elem = elems[i],
        rect = elem.getBoundingClientRect();
      if (rect.bottom >= offsetTop && rect.top <= winHeight - offsetBot) {
        elem.classList.add('inview');
        if (once) {
          count--;
          elems.splice(i, 1);
        }
      } else {
        elem.classList.remove('inview');
      }
    }
    ticking = false;
  }

  function onResize() {
    winHeight = window.innerHeight;
    requestTick();
  }

  function onScroll() {
    requestTick();
  }

  function requestTick() {
    if (!ticking) {
      requestAnimationFrame(update);
      ticking = true;
    }
  }
  window.addEventListener('resize', onResize, false);
  document.addEventListener('scroll', onScroll, false);
  document.addEventListener('touchmove', onScroll, false);
  onResize();
}
inView({
  selector: '.viewme', // an .inview class will get toggled on these elements
  once: false, // set this to false to have the .inview class be toggled on AND off
  offsetTop: 180, // top threshold to be considered "in view"
  offsetBot: 100 // bottom threshold to be considered "in view"
});
.box {
  width: 100%;
  height: 50vh;
  margin-bottom: 10px;
  background: blue;
  opacity: 0;
  transition: opacity .2s ease;
}

.inview {
  opacity: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>

您提供的 fiddle 只需稍作改动即可正常工作。您需要将观察器应用于所有元素才能正常工作。

看这个例子:

const config = {
  root: null,
  rootMargin: '0px',
  threshold: [0.1, 0.5, 0.7, 1]
};

let previousY = 0;
let previousRatio = 0;


let observer = new IntersectionObserver(function(entries) {
  entries.forEach(entry => {
    const currentY = entry.boundingClientRect.y
    const currentRatio = entry.intersectionRatio
    const isIntersecting = entry.isIntersecting
    const element = entry.target;

    element.classList.remove("outview-top", "inview-top", "inview-bottom", "outview-bottom");
    // Scrolling up
    if (currentY < previousY) {
      const className = (currentRatio >= previousRatio) ? "inview-top" : "outview-top";
      element.classList.add(className);

      // Scrolling down
    } else if (currentY > previousY) {
      const className = (currentRatio <= previousRatio) ? "outview-bottom" : "inview-bottom";
      element.classList.add(className);
    }

    previousY = currentY
    previousRatio = currentRatio
  })
}, config);

const images = document.querySelectorAll('.box');
images.forEach(image => {
  observer.observe(image);
});
.box {
  width: 100%;
  height: 50vh;
  margin-bottom: 10px;
  background: lightblue;
  transition: opacity .2s ease;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 3em;
}

[class*='inview'] {
  opacity: 1;
}

[class*='outview'] {
  opacity: 0.6;
}
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>