<div class="inner">
  <p class="text">スクロールして下さい↓↓</p>
  <div class="cols cols--2">
    <figure data-parallax-front>
      <img src="https://picsum.photos/800/500/?random=1" alt="画像" width="495" height="309" />
    </figure>
    <p>
      スクロールするとテキストは通常のスピードで移動し、画像はテキストより早く(大きく)移動します。
    </p>
  </div>
  <p class="text">スクロールして下さい↓↓</p>
  <div class="cols cols--2">
    <div data-parallax-front data-y="75px">
      <p>
        画像以外の要素を移動させることも可能です。テキストをy軸方向に75pxから-75pxへと移動させています。
      </p>
    </div>
    <figure>
      <img src="https://picsum.photos/800/500/?random=2" alt="画像" width="495" height="309" />
    </figure>
  </div>
  <p class="text">スクロールして下さい↓↓</p>
  <p class="memo">
    個別にy軸方向の移動量を調整するとより立体的な動きを表現できます。
  </p>
  <div class="cols cols--3">
    <figure class="img" data-parallax-front data-y="45%">
      <img src="https://picsum.photos/800/500/?random=3" alt="画像" width="310" height="465" />
    </figure>
    <figure class="img" data-parallax-front data-y="25%">
      <img src="https://picsum.photos/800/500/?random=4" alt="画像" width="310" height="465" />
    </figure>
    <figure class="img" data-parallax-front data-y="5%">
      <img src="https://picsum.photos/800/500/?random=5" alt="画像" width="310" height="465" />
    </figure>
  </div>
  <p class="text">終わり</p>
</div>
.inner {
  padding: 0 25px;
}

.cols {
  display: grid;
  grid-template-columns: repeat(var(--_cols_number), minmax(0, 1fr));
  column-gap: 20px;
  align-items: center;
}

.cols--2 {
  --_cols_number: 2;
}

.cols--3 {
  --_cols_number: 3;
}

img {
  width: 100%;
  height: auto;
  object-fit: cover;
}

.img img {
  aspect-ratio: 100 / 150;
}

.text {
  height: 100svh;
  display: grid;
  place-items: center;
  font-size: max(18px, 2.5vw);
}

.memo {
  margin-bottom: 100px;
  text-align: center;
}
const items = document.querySelectorAll("[data-parallax-front]"); // data-parallax-front属性を持つ要素を取得

// 各要素に対して処理を行う
for (const item of items) {
  const y = item.getAttribute("data-y") ?? "15%"; // itemのdata-y属性を取得。未設定の場合は'15%'をデフォルト値として使用
  const delay = parseFloat(item.getAttribute("data-delay")) || 0.4; // itemのdata-delay属性を数値に変換して取得。未設定の場合は'0.4'をデフォルト値として使用
  const target = item.children; // itemの直下の要素を取得

  gsap.fromTo(
    // gsap.fromToでアニメーションを設定
    target, // 対象の要素をtargetで指定
    {
      y: y // アニメーション開始時のy位置
    },
    {
      y: `-${y}`, // アニメーション終了時のy位置
      scrollTrigger: {
        // スクロールトリガーを設定
        trigger: item, // itemをトリガーとなる要素として指定
        start: "top bottom", // 要素の上端がビューポートの下端に達したときに開始
        end: "bottom top", // 要素の下端がビューポートの上端に達したときに終了
        scrub: delay // スクロールに追従してアニメーションを行う(デフォルトでは0.4秒の遅延)
      },
      ease: "none" // イージング関数を指定しない(線形アニメーション)
    }
  );
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://unpkg.com/gsap@3/dist/gsap.min.js
  2. https://unpkg.com/gsap@3/dist/ScrollTrigger.min.js