JSFiddle - React, Tailwind, and code Playground

by mamounothman

HTML

<div class="container">

<p class="hi">There should always be four blue boxes in one row. Scroll down and back up</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>
</div>

CSS

body {
  text-align: center;
}
.hi {
  padding: 40vh 0;
  background: lightblue;
  width: 100%;
}

.container {
  display:flex;
  flex-wrap: wrap;
}

.box {
  width: 23%; /* change this to 100% and it works fine */
  height: 40vh;
  margin-bottom: 10px;
  background: blue;
}

.viewme {
  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.15, 0.2, 0.25, 0.3]
};

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 viewbox = document.querySelectorAll('.viewme');
viewbox.forEach(image => {
  observer.observe(image);
});