JSFiddle - React, Tailwind, and code Playground

by Sub Lines

HTML

<p class="hi">Scroll down</p>
<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>
<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>

CSS

.hi {
  padding: 40vh 0;
}
.box {
  width: 100%;
  height: 40vh;
  margin-bottom: 10px;
  background: blue;
  transition: opacity .2s ease;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 3em;
  
  opacity: 0;
  transform: translateY(20px);
  transition: all .3s ease;
}

.inview-top, .inview-bottom {
  opacity: 1;
  transform: translateY(0);
}

.outview-top {
  opacity: 0;
  transform: translateY(-20px);
}
.outview-bottom {
  opacity: 0;
  transform: translateY(20px);
}

JavaScript

const config = {
  root: null,
  rootMargin: '0px',
  threshold: [0.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('.viewme');
images.forEach(image => {
  observer.observe(image);
});