<div class="container">
  <div class="box js-scrollIn"></div>
  <div class="box js-scrollIn"></div>
  <div class="box js-scrollIn"></div>
  <div class="box js-scrollIn"></div>
  <div class="box js-scrollIn"></div>
  <div class="box js-scrollIn"></div>
  <div class="box js-scrollIn"></div>
  <div class="box js-scrollIn"></div>
</div>
@charset "UTF-8";

.container {
  padding: 40px 0;
}
.box {
  width: 80%;
  max-width: 600px;
  margin: 0 auto;
  background-color: #ddd;
  aspect-ratio: 5 / 2;
  + .box {
    margin-top: 8rem;
  }
}

.js-scrollIn {
  transform: translateY(30px);
  opacity: 0;
  transition: all 0.8s ease;
}
.js-scrollIn.active {
  transform: translateY(0);
  opacity: 1;
}
View Compiled
// DOMの読み込みが完了したらhandleScrollIn関数を実行
document.addEventListener("DOMContentLoaded", () => {
  handleScrollIn();
});

// スクロールアニメーションを処理する関数
const handleScrollIn = () => {
  // .js-scrollInクラスを持つ全ての要素を取得
  const targets = document.querySelectorAll(".js-scrollIn");

  // Intersection Observerのオプションを設定
  const options = {
    root: null, // ビューポート全体を監視対象とする
    rootMargin: "-20% 0px", // 上下20%のマージンを設定(要素が20%見えたら発火)
    threshold: [0] // 交差が始まった時点でコールバックを実行
  };

  // 交差時に実行されるコールバック関数
  const callback = (entries, observer) => {
    entries.forEach((entry) => {
      // 要素が交差したら
      if (entry.isIntersecting) {
        // activeクラスを追加してアニメーションを開始
        entry.target.classList.add("active");
        // 一度アニメーションが開始したら、その要素の監視を終了
        observer.unobserve(entry.target);
      }
    });
  };

  // Intersection Observerインスタンスを作成
  const observer = new IntersectionObserver(callback, options);

  // 全てのターゲット要素に対してIntersection Observerを設定
  targets.forEach((target) => {
    observer.observe(target);
  });
};
Run Pen

External CSS

  1. https://unpkg.com/@acab/reset.css

External JavaScript

This Pen doesn't use any external JavaScript resources.