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サイト制作を行う時には必須で意識したいことです。
基本的に画面外のアニメーションは停止させることを意識しましょう!


