🤔

GSAP ScrollTriggerとIntersection Observer APIどちらを使うべきか

2023/02/11に公開

今回作ってみたもの

もう2月も半ばに入りましたが、今年の目標を書きました。
スクロールに連動してこんな感じで動きます。

初めはGSAP ScrollTriggerで作っていたのですが「これ IntersectionObserverAPI でもできそうだな?どっちがいいんだろう?」という疑問が湧いたので、両方作って比較してみました。

GSAP ScrollTriggerの場合

スクロールして見てください。

左側の数字を切り替える

JavaScript
const num = document.querySelector(".num");
const blocks = gsap.utils.toArray(".resolutionBlock");

window.addEventListener("load", () => {
  blocks.forEach((block, i) => {
    ScrollTrigger.create({
      trigger: block,
      start: "top 50%",
      bottom: "bottom 50%",
    });
    gsap
      .timeline({
        scrollTrigger: {
          trigger: block,
          start: "top 50%",
          end: "bottom 50%",
          scrub: true,
        },
      })
      .to(num, {
        onComplete: () => {
          if (num.textContent < blocks.length) {
            //5未満の時
            num.innerHTML = `0${parseInt(num.textContent, 10) + 1}`;
          }
        },
        onReverseComplete: () => {
          if (num.textContent > 1 && num.textContent <= blocks.length) {
            //1以上5未満の時
            num.innerHTML = `0${parseInt(num.textContent, 10) - 1}`;
          }
        },
      });
  });

対象となる要素(今回だと.resolutionBlock)が複数あるのでforEachで回して、
timelineのコールバック関数で数字を足したり引いたりしています。
onCompleteはアニメーション完了時に実行されますが、
onReverseCompleteだとアニメーションが逆方向から再び開始に達したときに呼び出されます。
今回だと上から下にスクロールした時にonComplete、下から上にスクロールした時にonReverseCompleteが呼び出されるわけですね。
カウントされる数字に上限と下限を持たせるために判定式も書いています。

SVGを回転させる

JavaScript
const numWrap = document.querySelector(".numWrap");
window.addEventListener("load", () => {
  //SVGを回転させる
  const rotate_anime = gsap.timeline({
    scrollTrigger: {
      trigger: numWrap,
      start: "top 50%",
      end: () => `+=${document.querySelector(".resolutionWrap").offsetHeight}`,
      pin: true,
      scrub: true,
    },
  });
  rotate_anime.to(".numWrap svg", {
    rotate: 720,
  });
});

数字部分をターゲットに指定し、pin: trueすることで固定にしています。
右側のセクション(.resolutionWrap)のスクロールが終わるまでの間アニメーションさせたいので、endの位置をoffsetHeightで取得しています。こうすることでレスポンシブにも対応できるので便利ですよね。

Intersection Observer APIの場合

スクロールして見てください。
※Codepenで見ると判定のタイミングが少し早いですね、、

左側の数字を切り替える

HTML
<!-- ...略 -->
<div class="resolutionBlock" data-num="1">
  <p class="resolutionTitle">転職する</p>
  <p class="resolutionText">
    転職活動中です。転職活動中です。転職活動中です。転職活動中です。転職活動中です。転職活動中です。転職活動中です。転職活動中です。転職活動中です。転職活動中です。
  </p>
</div>
<div class="resolutionBlock" data-num="2">
  <p class="resolutionTitle">WebGL頑張る</p>
  <p class="resolutionText">
    WebGLは楽しいけどむずかしいのでコツコツとやっていきたいです。WebGLは楽しいけどむずかしいのでコツコツとやっていきたいです。WebGLは楽しいけどむずかしいのでコツコツとやっていきたいです。WebGLは楽しいけどむずかしいのでコツコツとやっていきたいです。WebGLは楽しいけどむずかしいのでコツコツとやっていきたいです。
  </p>
</div>
<!-- ...略 -->
JavaScript
const blocks = document.querySelectorAll(".resolutionBlock");
const numWrap = document.querySelector(".numWrap");
const svg = document.querySelector(".numWrap svg");
const num = document.querySelector(".num");

const options = {
  root: null,
  rootMargin: "0px 0px -90% 0px",
  threshold: 0,
};

const observer = new IntersectionObserver(doWhenIntersect, options);
blocks.forEach((block) => {
  observer.observe(block);
});

function doWhenIntersect(entries) {
  entries.forEach((entry, index) => {
    if (entry.isIntersecting) {
      num.innerHTML = `0${entry.target.dataset.num}`;
      numWrap.classList.add("is-active");
    }
  });
}

ScrollTriggerの場合と違い、.resolutionBlockにdata属性で数字を持たせました。
optionで指定したルート要素(ビューポート)と、監視対象のターゲット(.resolutionBlock)が交差したら、数字部分にそれぞれのdata-numを吐き出しています。

SVGを回転させる

JavaScript
//...略
function doWhenIntersect(entries) {
  entries.forEach((entry, index) => {
    if (entry.isIntersecting) {
      rotateAnim();
    }
  });
}

function rotateAnim() {
  window.addEventListener("scroll", function () {
    svg.style.transform =
      "translate(-50%, -50%) rotate(" + window.pageYOffset + "deg)";
  });
}

scrollイベントでwindow.pageYOffsetを取得してtransformの値に代入しています。

結論

今回のケースではそこまで複雑なアニメーションをしているわけではないので、JavaScriptの記述量も少なく、ブラウザに負荷が掛かりづらいIntersectionObserverAPIを選んだ方が良いのでは?と個人的には感じました。
しかし、実務ではクライアントやディレクターから後になって思わぬ追加要望が来ることも大いに考えられますよね。そんな時のためにあらかじめ機能の豊富なGSAPで作っておいた方が安心かもしれません。GSAP最強!GSAP万歳!GSAPいつもありがとう!
もっとこうした方がいいよ、とかあればコメントいただけると嬉しいです☺

Discussion