スクロールに対応したアニメーションはウェブ制作においてよく求められる機能要件でもあります。JavaScriptのIntersectionObserverAPI(参考記事:
JavaScriptのIntersectionObserverAPIでビューポート内での表示を検知する)とCSSを併用することで実装できるのですが、どうしても各コードが増えがちで実装時に面倒だったり、あとから改修する場合にコードの見通しが悪くなってしまうなどのデメリットがあります。そこでアニメーションライブラリのGSAPのプラグインである「ScrollTrigger」使い、シンプルかつ簡単にアニメーションに対してスクロールのトリガーを設定してみたいと思います。ScrollTriggerではいろんなスクロールのトリガーを設定できますが、今回は基本的な使い方をメインにまとめてみました。
GSAPはとてもメジャーなアニメーションのライブラリで、導入方法や基本的な使い方については過去記事
「JavaScriptのアニメーションライブラリ「GSAP」を使ってみる」にて詳しくまとめていますのでご参考に。
では早速このプラグインの導入方法から見ていきたと思います。まずベースとなるHTMLですが下記のようにアニメーションのトリガー要素の中に、アニメーション要素が入っているような構成を例に見ていきます。トリガー要素がスクロールによって特定位置までくると、アニメーション要素に設定したアニメーションが実行されるイメージですね。後半ではアニメーション要素とトリガー要素が同じケースで紹介しているものもあります。
// トリガー要素とアニメーション要素が別
<div class="js-animation-trigger">
<div class="js-animation-element"></div>
</div>
// トリガー要素とアニメーション要素が同じ(アニメーションさせる要素をトリガーにする)
<div class="js-animation-trigger js-animation-element"></div>
まずはソースコードをインポートします、npmなどでインストールした場合にはScrollTriggerのプラグインも含まれているので、下記のようにGSAP本体とScrollTriggerプラグインをインポートしていきます。
import { gsap } from 'gsap';
import ScrollTrigger from 'gsap/ScrollTrigger';
これらはCDNでも提供されているので、CDNで利用する場合には下記のように読み込んでいきます。
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.8.0/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.8.0/ScrollTrigger.min.js"></script>
では実際の処理を見ていきます。ScrollTriggerに限らずですがGSAPでプラグインを使う場合には、最初に「gsap.registerPlugin()」で使用するプラグインを登録しておく必要があります。引数の中に指定していき、複数ある場合にはコンマ区切りになります。それに続いていつも通りのトゥイーンの処理を書いていきます。その時に「scrollTrigger」オプションを指定することで、アニメーションに対してスクロールでのトリガーを関連付けすることができます。
gsap.registerPlugin(ScrollTrigger);
gsap.fromTo('.js-animation-element',
{
y: 50,
autoAlpha: 0
},
{
y: 0,
autoAlpha: 1,
delay: 0.6,
duration: 1.2,
ease: 'power2.out',
scrollTrigger: {
trigger: '.js-animation-trigger',
start: 'top center'
}
}
);
たったこれだけで、スクロールに対応したトリガーが設定されたアニメーションを実装することができます。IntersectionObserverAPIとアニメーション用のCSSを設定することを考えるとかなりラクですよね。このscrollTriggerオプションではいろんな指定ができますが、今回は基本的なものについて下記をまずは押さえておきたいですね。
| scrollTrigger.trigger |
アニメーションを発火させるトリガー要素を指定、ここで指定した要素の位置がアニメーション発火の基準とされます。アニメーション要素自体をトリガーにする場合には、アニメーション要素を指定する。 |
| scrollTrigger.start |
アニメーションの開始位置を指定、最初の値ではトリガー要素の基準位置を、2番目の値ではスクローラーの基準位置を指定します。 |
スクローラーと呼ばれるトリガーの対象となる領域ですが、デフォルトではビューポートになります。ですので、start: 'top center' の場合には、トリガー要素の上端がビューポートの中央に交差したタイミングでアニメーションが発火するということになります。
また、仕様によっては以下のように同じアニメーションが複数箇所にわたる場合もあります。
<div class="js-animation-trigger">
<div class="js-animation-element"></div>
</div>
....
<div class="js-animation-trigger">
<div class="js-animation-element"></div>
</div>
....
<div class="js-animation-trigger">
<div class="js-animation-element"></div>
</div>
....
その場合には、アニメーション要素を全て取得して、forEach()メソッドでそれぞれgsapの処理を行なっていきます。その際にトリガー要素はそれぞれ対応したものにさせないと一度に実行されてしまうので、引数を使う形になります。
const items = gsap.utils.toArray('.js-animation-element');
items.forEach((item) => {
gsap.fromTo(item,
{
y: 50,
autoAlpha: 0
},
{
y: 0,
autoAlpha: 1,
delay: 0.6,
duration: 1.2,
ease: 'power2.out',
scrollTrigger: {
trigger: item.parentNode,
start: 'top center'
}
}
);
});
上記のように「gsap.utils.toArray()」メソッドを使うことで、対象要素を配列にまとめてくれるので、繰り返し処理で使う場合にはとても便利ですね。
ScrollTrigger.batch()で複数のトリガー要素自身に対してまとめて同じ条件のトリガーを設定する
先ほどのforEachを使う方法ですが、トリガー要素とアニメーション要素が同じ場合には「ScrollTrigger.batch()」メソッドを使うことで、同じ条件のトリガーをバッチ処理のように設定して繰り返しすことができます。第一引数にはこれまで同様にアニメーションの対象要素を、第二引数にはアニメーションをコールバック関数として記述します。
ScrollTrigger.batch('.js-animation-element', {
onEnter: batch => gsap.fromTo(batch,
{
y: 50,
autoAlpha: 0
},
{
y: 0,
autoAlpha: 1,
delay: 0.6,
duration: 1.2,
ease: 'power2.out'
}
),
onLeave: batch => gsap.set(batch,
{
background: 'red',
color: 'white'
}
),
once: true
});
コールバック関数を設定する際には「onEnter」「onLeave」などのキーで指定する形になり、順方向のスクロールでスクローラー領域に入った時や外れた時(逆方向でスクローラーに入った時)とそれぞれ個別に処理を適用することができます。
| onEnter |
準方向(下方向)へのスクロールでトリガー要素がスクローラーに入った時に実行 |
| onLeave |
準方向(下方向)へのスクロールでトリガー要素がスクローラーから外れた時に実行 |
| onEnterBack |
逆方向(上方向)へのスクロールでトリガー要素がスクローラーに入った時に実行 |
| onLeaveBack |
逆方向(上方向)へのスクロールでトリガー要素がスクローラーから外れた時に実行 |
また、このコールバック関数はデフォルトでは何度も繰り返されるため、1回だけ実行させたい場合には、「once」オプションで「true」を指定する必要があります。ですので上記のサンプルコードでは、ビューポートにトリガー要素が入るとアニメーションが1度だけ実行され、順スクロールに入ると要素が赤背景で白文字に変わるという処理になります。
ScrollTrigger.batch()でstaggerオプションを使ったシーケンシャルなアニメーション
この処理は複数要素に対して一括でトリガーが設定されたアニメーションを実装するときに有効ですが、連続した要素として並んでいる場合に、アニメーションのタイミングを遅延させてずらすことでシーケンシャルなアニメーションにしたいこともあるかと思います。そんな場合には同じ処理で、「stagger」のオプションで遅延させる時間を設定することができます。下記のサンプルでは0.2秒ずつ遅延されてアニメーションが実行され、シーケンシャルな動きを表現することができます。合わせて「batchMax」のオプションでは、バッチ処理で許可される最大の要素数が指定できます。上記では3つずつの要素毎に遅延の動きが実現できるようになります。
ScrollTrigger.batch('.js-animation-element', {
batchMax: 3,
onEnter: batch => gsap.fromTo(batch,
{ y: 50, autoAlpha: 0 },
{ y: 0, autoAlpha: 1, stagger: 0.2 }
),
onLeave: batch => gsap.set(batch,
{ y: 50, autoAlpha: 0, overwrite: true }
),
onEnterBack: batch => gsap.fromTo(batch,
{ y: 50, autoAlpha: 0 },
{ y: 0, autoAlpha: 1, stagger: 0.2 }
),
onLeaveBack: batch => gsap.set(batch,
{ y: 50, autoAlpha: 0, overwrite: true }
),
});
また、上記のようにスクローラーの出入り両方のタイミングで処理を行う場合、スクロールのスピードではアニメーションが完了状態にならず、次のアニメーションが始まるため、途切れた動きになってしまいます。そんな場合には「overwrite」のオプションを「true」にすることでアニメーションが途中であっても、次の処理で上書きされるように実行されるため、スムーズな動きで繋げることができますので、合わせて覚えておきたいですね。
今回のScrollTriggerプラグインを使った
サンプルはこちらに用意していますので、また動作や挙動の参考にしていただければと思います。
今回はGSAPのScrollTriggerプラグインを使って、スクロールに対応したトリガーをアニメーションに設定する方法についてまとめてみました。ここでは基本的なものだけで、もっと複雑なアニメーションも実装することができますが、これだけでもいろんな場面でかなり便利に使えるのではないでしょうか。また応用編については改めて記事にまとめてみたいと思います。
(参考にさせて頂いたサイト)
GreenSock - ScrollTrigger