本文へ移動

Intersection Observer で画面外のCSSアニメーションを停止してパフォーマンスを改善する


CSSアニメーションのanimation-iteration-count: infiniteを指定している要素で、画面外でも無制限にアニメーションを繰り返しているWEBサイトをよく見かけます。

ブラウザは画面外のアニメーションもケアしないと実行され続けるので、CPU・GPUリソースは消費され続けます。画面外での不要なアニメーションを制御してパフォーマンスのよいWEBサイトを制作しましょう。

Intersection Observer で画面外のCSSアニメーションを制御するコードと実装例

Intersection Observer を使用して画面内・画面外の判定をする

要素が画面外の時の判定はIntersection Observerを使用します。

https://developer.mozilla.org/ja/docs/Web/API/IntersectionObserver

scrollイベントではなくIntersection Observerを使用することでターゲット要素がビューポート(または指定したルート要素)に「交差」したかどうかを効率的に監視することができます。

const targets = document.querySelectorAll('.js-intersection');

const io = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      // 要素が画面内に入った場合
      entry.target.classList.add('is-intersection');
    } else {
      // 要素が画面外に出た場合
      entry.target.classList.remove('is-intersection');
    }
  });
}); // オプションは省略してデフォルト値を使用する。要素がビューポートに少しでも入るか出るかしたときに実行される。

targets.forEach((target) => io.observe(target));

scrollイベント で実装した場合の懸念点

scrollイベントでも同様にターゲット要素の画面内・画面外の判定は行えますが、scrollイベントはスクロールする度に頻繁に発火します。逆にパフォーマンスが悪化してしまう可能性が高いためIntersection Observerで実装しましょう🙆‍♂️

animation-play-state でCSSアニメーションの制御をする

CSSアニメーションの制御にはanimation-play-stateを使用します。

  • running :アニメーションが現在実行中です。
  • paused :アニメーションが現在停止中です。

https://developer.mozilla.org/ja/docs/Web/CSS/animation-play-state

初期状態ではアニメーションを停止させておき、要素が画面内に侵入した際に付与されるis-intersectionクラスをトリガーとしてアニメーションを実行します。

.animation {
 animation: oneturn 10s linear infinite;
 animation-play-state: paused;
}

.animation.is-intersection {
 animation-play-state: running;
}

@keyframes oneturn {
 100% {
  transform: rotate(oneturn);
 }
}

【応用】 Intersection Observer でスクロールイベントの制御をする

Intersection Observerは、CSSアニメーションの制御だけでなく、scrollイベントでスクロール値に連動した計算を必要とする要素にも実装することで、画面外の時に不要なイベントハンドリングを削除してパフォーマンスを改善することができます✅

const target = document.querySelector('.js-scroll-target');

const handleScroll = () => {
  // スクロール値に連動した計算処理をしたい場合
  const scrollY = window.pageYOffset;
  target.style.transform = `translateY(${scrollY}px)`;
};

const io = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      // 要素が画面内に入った場合
      window.addEventListener("scroll", handleScroll);
    } else {
      // 要素が画面外に出た場合
      window.removeEventListener("scroll", handleScroll);
    }
  });
});

io.observe(target);

まとめ

個人的にクリエイティブなWEBサイト制作を行う時には必須で意識したいことです。

基本的に画面外のアニメーションは停止させることを意識しましょう!