GSAP ScrollTriggerとIntersection Observer APIどちらを使うべきか
今回作ってみたもの
もう2月も半ばに入りましたが、今年の目標を書きました。
スクロールに連動してこんな感じで動きます。
初めはGSAP ScrollTriggerで作っていたのですが「これ IntersectionObserverAPI でもできそうだな?どっちがいいんだろう?」という疑問が湧いたので、両方作って比較してみました。
GSAP ScrollTriggerの場合
スクロールして見てください。
左側の数字を切り替える
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を回転させる
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で見ると判定のタイミングが少し早いですね、、
左側の数字を切り替える
<!-- ...略 -->
<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>
<!-- ...略 -->
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を回転させる
//...略
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