• 最終更新日:

GSAPのScrollTrigger.jsを使ったスクロールアニメーションを実装する方法

GSAPのScrollTrigger.jsを使ったスクロールアニメーションを実装する方法

今回はGSAPのScrollTrigger.jsを使ったスクロールアニメーションについて説明します。

前提条件は以下

  • 複雑なアニメーションをしたい
  • アニメーションをサクサク動かしたい
  • CSSは操作せず、JavaScriptだけでアニメーションさせたい

説明環境は以下

  • macOS Catalina v10.15.5
  • Visual Studio Code v1.57.0
この記事の目次

GSAPのScrollTrigger.jsを使ったスクロールアニメーションを実装する方法

簡単なスクロールアニメーションであれば、Intersection Observerを使えばブラウザに負担をかけずに実装できます。ただ、複雑なアニメーションがいくつもある場合はScrollTrigger.jsがオススメです。

Intersection Observerの使い方については以下の記事をどうぞ。

ScrollTriggerで実装できるアニメーションについては以下の公式から確認できます。

ScrollTrigger.jsを読み込む

GSAP本体とScrollTrigger.jsの2つを読み込みます。CDNから読む込む場合は以下を書きます。

<script src="//cdn.jsdelivr.net/npm/gsap@3.7.0/dist/gsap.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/gsap@3.7.0/dist/ScrollTrigger.min.js"></script>

基本的な書き方

イメージをつかむために基本的な書き方を説明します。
以下のデモページで動きを確認してください。

gsap.to('.js-demo-section', { //アニメーションしたい要素を指定
  x: 800, //横に800px動かす
  scrollTrigger: {
    trigger: '.js-trigger',//アニメーションが始まるトリガーとなる要素
    start: 'top center', //アニメーションが始まる位置を指定
  }
});

クラス名js-trigger要素のトップが、ブラウザの中央部分と重なると、クラス名js-demo-section要素が右に800px動く、、という内容です。

イメージとはしては以下です。

アニメーションが発火する位置についてはstartで指定されています。

gsap.to('.js-demo-section', { //アニメーションしたい要素を指定
  x: 800, //横に800px動かす
  scrollTrigger: {
    trigger: '.js-trigger',//アニメーションが始まるトリガーとなる要素
    start: 'top center' //アニメーションが始まる位置を指定
  }
});

topとは、triggerとして設定した.js-triggerのトップ部分を指していて、centerはブラウザ側の中央部分を指しています。この2つのポイントが重なることでアニメーションが始まります。

アニメーションの開始位置、終了位置を目で確認したい

どこからアニメーションが開始して、どこで終わるのか目で確認したい場合は以下のように指定します。

gsap.to('.js-demo-section', { 
  x: 800,
  scrollTrigger: {
    trigger: '.js-trigger',
    start: 'top center' 
    markers: true,
  }
});

markes:trueを指定すると以下のように表示されて、アニメーションの調整が容易になります。scroller-startとstarが重なるとアニメーションが起こり、scroller-endとendが重なるとアニメーションが終了します。

アニメーション前の状態を指定したい場合

例えば透過した状態からアニメーションさせたい場合など、特定の状態からアニメーションさせたい場合は以下のように書きます。

gsap.set('.js-demo-section', {autoAlpha: 0}); //初期状態としてopacity: 0;とvisibility: hidden;が指定される

gsap.to('.js-demo-section', { 
  autoAlpha: 1, //opacity: 1;とvisibility:visible;がつく
  scrollTrigger: {
    trigger: '.js-trigger',
    start: 'top center'
  }
});

gsap.setで初期状態を設定できます。
autoAlpha:0を指定すると自動でopacity: 0;とvisibility: hidden;が指定されます。

gsap.fromToを使えば初期状態からアニメションまで連続して書くことができます。

gsap.fromTo('.js-demo-section', { 
  autoAlpha: 0, //ここで初期状態を設定
  },
  {
  autoAlpha: 1, //ここでアニメーションさせたい内容を書く
    scrollTrigger: {
      trigger: '.js-trigger',
      start: 'top center'
    }
  }
});

アニメーションをどの状態からスタートさせるか、以下の4つで指定できます。それぞれの説明は公式サイトのリンクを貼っておきます。

  • gsap.set … アニメーション前の状態を指定できる
  • gsap.to … 現状の状態からアニメーションさせる
  • gsap.fromTo … 初期状態をセットして、そこからアニメーションさせる
  • gsap.from … 指定した状態から現状の状態へアニメーションする

指定できるアニメーション

ほとんどのCSSをアニメーションで指摘できます。以下は例です。

gsap.to('.js-demo-section', { 
  autoAlpha: 1, //opacity: 1;とvisibility:visible;がつく
  x: 100, //右に100px移動。-100で左に100px移動。
  y: 200, //下に200px以上。-200で上に200px移動。
  scale:2, //2倍にする。scaleXとscaleYも指摘できる
  rotation: -30, //回転。rotationXとrotationYも指定できる
  skewX: "30deg", //変形 skewYも使える。skewは使えない。 
  delay: 2, //2秒後にアニメーションさせる
  duration: 5, //5秒後かけてアニメーションさせる
  backgroundColor: "red", //背景色を赤にする
  …
});

cssでbackground-colorと指定されていたものは、backgroundColorと指定します。border-radiusならborderRadiusと指定します。

詳しくは公式で確認してみてください。

複数の要素をアニメーションさせる

複数の要素をアニメーションさせる場合は以下です。

gsap.to('.js-demo-section, .js-demo-section02, .js-demo-section03', { 
  x:100,
  scrollTrigger: {
    trigger: '.js-trigger',
    start: 'top center'
  }
});

カンマで区切って複数指定します。
この場合はすべて同じタイミングでアニメーションします

同じクラス名がついた要素が並んでいて、端から順番にアニメーションさせる場合については以下です。クラス名js-demo-section1の要素を左から順番に時差をつけてアニメーションします。

<section class="d-demo__section">
  <div class="d-demo__main js-trigger1">
      <div class="d-demo__wrap">
          <div class="d-demo__elem js-demo-section1"></div>
          <div class="d-demo__elem js-demo-section1"></div>
          <div class="d-demo__elem js-demo-section1"></div>
          <div class="d-demo__elem js-demo-section1"></div>
          <div class="d-demo__elem js-demo-section1"></div>
      </div>
  </div>
</section>

JavaScript側では以下のように指定します。

gsap.to('.js-demo-section1', {
 x: 100,
 scrollTrigger: {
    trigger: '.js-trigger1',
    start: 'top center'
  },
  stagger: {
    from: "start", //左からアニメーション start、center、edges、random、endが指定できる
    amount: 0.1 //0.1秒ズラしてアニメーション
     }
});

straggerのfromでは、どの順番でアニメーションをしていくか指定できます。

  • start … 頭から始める
  • center … 中央から始める
  • edges … 両端から始める
  • random … ランダムに始める
  • end … 最後から始める

staggerについてもっと知りたい方は公式を参照してください。

それぞれ交差するタイミングでアニメーションする。

アニメーション開始のtriggerとなる要素と、アニメーションする要素が一緒のときはbatchを使うことで実装できます。once: trueを指定していないと、アニメーションが起こる場所に来ると何度も処理が行われてしまうので注意しましょう。

gsap.set(".js-demo-section04", {opacity:0,y: 100}); //初期状態をセット

ScrollTrigger.batch(".js-demo-section04", {
  onEnter: batch => gsap.to(batch, {opacity: 1, y: 0,}),
  start: "top 50%",
  once: true //この指定によって1度だけアニメーションされる
});

違う書き方では、アニメーションさせる要素をループ処理して実装する方法もあります。

const elems = document.getElementsByClassName('js-demo-section2');

for (let i = 0; i < elems.length; i++) {
  gsap.to(elems[i], {
    x: 100,
      scrollTrigger: {
        trigger: elems[i],
        start: 'top center'
       }
  }); 
}

アニメーションのスピードを指定する(Ease)

Ease系の指定はたくさん用意されています。
まずは以下のページを開きましょう。

以下のようにスピード選ぶことができます。
デフォルトのスピードの指定はpower1.outです。

自分でスピードをカスタマイズしたい場合は、以下のように操作します。

①でベースとなるスピードを選んだら【 Custom 】ボタンを押します。そうすると左側のカーブが操作できるようになるので、好きなカーブを作ります。カスタムしたスピードは④からコピペして使います。

以下は例です。

gsap.to('.js-demo-section', {
  x: 800,
  duration: 2, //2秒かけてアニメーション
  ease: CustomEase.create("custom", "M0,0,C0.126,0.382,0.334,0.41,0.492,0.558,0.684,0.738,0.818,1.001,1,1"),
  scrollTrigger: {
    trigger: '.js-trigger',//アニメーションが始まるトリガーとなる要素
    start: 'top center' //アニメーションが始まる位置
  }
});

スクロール量に合わせて、アニメーションを進める

アニメーションが発火したら最後までアニメーションさせるのではなく、スクロール量とアニメーションの進捗を合わせることができます。

gsap.to('.js-demo-section', {
  x: 800,
  scrollTrigger: {
    trigger: '.js-trigger',//アニメーションが始まるトリガーとなる要素
    start: 'top center', //アニメーションが始まる位置
    end: 'bottom center', //アニメーションが終わる位置
    scrub: true, //スクロール量に合わせてアニメーションが進む(数字も指定できる)
  }
});

endでスクロールがどこまで進んだらアニメーションを終了させるか指摘できます。
scrub:trueにするとスクロール量に合わせてアニメーションが進みます。

scrub:1など数字を入れるとスクロール量にあわせて、1秒間余韻をもって動き続けます。

特定の位置で要素を固定して、アニメーションする

特定の位置で要素を固定して、さらにその中の要素をアニメーションさせることができます。

gsap.to('.js-demo-section', {
  x: 800,
  scrollTrigger: {
    trigger: '.js-trigger',//アニメーションが始まるトリガーとなる要素。この要素が固定される
    start: 'top center', //アニメーションが始まる位置
    end: '+=1000', //アニメーション開始位置から1000px固定する
    pin: true //トリガー要素を固定する
  }
});

startの位置からtriggerで指定した要素が固定されます。
endでどこまで固定させるか指定できます。

特定の位置でクラス名を追加・削除する

特定の位置でクラス名をつけて、特定の位置が過ぎたらクラス名を削除することができます。

ScrollTrigger.create({
    trigger: '.js-trigger', //アニメーションが始まるトリガーとなる要素
    start: 'top center',
    end: 'bottom center', 
    toggleClass: {targets: ".js-demo-section, js-demo-section02", className: "is-active"}, //クラスをつけたり、外したりできる
  });

triggerに指定された要素が、startの位置にきたときに、targetsで指定した要素に、is-activeのクラス名を追加して、endの位置が過ぎるとis-activeのクラス名を削除します。

クラス名を追加した後、削除したくない場合はonce:trueを追加しましょう。

ScrollTrigger.create({
    trigger: '.js-trigger', //アニメーションが始まるトリガーとなる要素
    start: 'top center',
    end: 'bottom center', 
    toggleClass: {targets: ".js-demo-section, js-demo-section02", className: "is-active"}, //クラスをつけたり、外したりできる
    once: true
  });

クラス名を追加するだけなら、toggleClassは使わず、onEnterでも指定できます。複数の要素にクラス名を追加するときはループ処理してください。

ScrollTrigger.create({
    trigger: '.js-trigger', //アニメーションが始まるトリガーとなる要素
    start: 'top center',
    onEnter: () => document.querySelector('.js-demo-section').classList.add('is-active'), 
  });

複雑なアニメーションを作る

複数のアニメーションを、細かくアニメーションさせることもできます。
具体的な書き方としては以下です。

const custom_anime = gsap.timeline({
  scrollTrigger: {
    trigger: ".js-trigger", //アニメーションが始まるトリガーとなる要素
    start: "top center"
  }
});

custom_anime.to('.js-demo-section1', //アニメーションする要素
  { keyframes: [
    { duration: 0.5, x: 100, y:200 }, //このアニメーションが終わったら下のアニメーションが起こる
    { duration: 1, x: 300, y:400} 
  ]}
);

gsap.timelineを使い、どの位置でアニメーションさせるかなど基本的な指定をしておきます。

どの要素に、どのようなアニメーションをさせるかの指定は9行目〜14行目で指定しています。keyframesで指定したアニメーションは上から順番に発火していきます。

gsap.timelineの使い方は公式を参照してください。

複数の要素を、バラバラにアニメーションさせる

特定の位置に来たときに、複数の要素をバラバラにアニメーションさせる場合は以下です。

const custom_anime = gsap.timeline({
  scrollTrigger: {
    trigger: ".js-trigger", //アニメーションが始まるトリガーとなる要素
    start: "top center"
  }
});

/*---1つ目の要素---*/
custom_anime.to('.js-demo-section1', //アニメーションする要素
  { keyframes: [
    { duration: 0.5, x: 100, y:200 }, //このアニメーションが終わったら下のアニメーションが起こる
    { duration: 1, x: 300, y:400} 
  ]}
);
/*---2つ目の要素---*/
custom_anime.to('.js-demo-section2', //アニメーションする要素
  { keyframes: [
    { duration: 4, delay:1, scale:2, rotation: -30 }, //このアニメーションが終わったら下のアニメーションが起こる
    { duration: 2, backgroundColor: "blue", borderRadius:"50%"} 
  ]}
);

1つの要素に続けて、別の要素を指定して書くだけです。ただこの書き方だと1つ目の要素のアニメーションが終わってから、2つ目の要素のアニメーションが始まります

要素同士のアニメーションのタイミングを調整する

1つ目の要素と2つ目の要素を同じタイミングでアニメーションさせる場合は以下です。

const custom_anime = gsap.timeline({
  scrollTrigger: {
    trigger: ".js-trigger", //アニメーションが始まるトリガーとなる要素
    start: "top center"
  }
});

/*---1つ目の要素---*/
custom_anime.to('.js-demo-section1', //アニメーションする要素
  { keyframes: [
    { duration: 0.5, x: 100, y:200 }, //このアニメーションが終わったら下のアニメーションが起こる
    { duration: 1, x: 300, y:400} 
  ]}
);
/*---2つ目の要素---*/
custom_anime.to('.js-demo-section2', //アニメーションする要素
  { keyframes: [
    { duration: 4, delay:1, scale:2, rotation: -30 }, //このアニメーションが終わったら下のアニメーションが起こる
    { duration: 2, backgroundColor: "blue", borderRadius:"50%"} 
  ]}, "<");

21行目にある"<"によって、1つ目の要素と同じタイミングでアニメーションします。

それ以外の指定では以下のものがあります。

">" //すぐ後の要素と同じタイミングでアニメーション
"4" // 4秒後にアニメーション
"+=1" //自分を基準に1秒後にアニメーション
"-=3" //自分を基準に3秒前にアニメーション
"<4" //前の要素を基準に4秒後にアニメーション
">2" //自分の後にある要素を基準に2秒後にアニメーション

言葉だといまいちアニメーションが起こるタイミングがわかりずらいので、以下の公式から動きを確認してみてください。

アニメーションを繰り返したい

アニメーションを繰り返しループさせることもできます。

const custom_anime = gsap.timeline({
  repeat: -1, // アニメーションの繰り返し回数。-1で無限回
  repeatDelay: 0.6, // ループとループの間の時間
  scrollTrigger: {
    trigger: ".js-trigger", //アニメーションが始まるトリガーとなる要素
    start: "top center"
  }
});

/*---1つ目の要素---*/
custom_anime.to('.js-demo-section1', //アニメーションする要素
  { keyframes: [
    { duration: 0.5, x: 100, y:200 }, //このアニメーションが終わったら下のアニメーションが起こる
    { duration: 1, x: 300, y:400} 
  ]}
);

repeatで繰り返しの回数を指定できます。repeatDelayはもう1度アニメーションを開始するまでの時間を指定します。

アニメーション開始位置、終了位置を過ぎたあとの処理

デフォルトではアニメーション開始位置を過ぎたら1度だけ発火しますが、アニメーション終了位置を過ぎたあとの処理や、上にスクロールで戻ったときの処理なども指定できます。

const custom_anime = gsap.timeline({
  toggleActions: 'play none none none', //デフォルトの指定
  scrollTrigger: {
    trigger: ".js-trigger", //アニメーションが始まるトリガーとなる要素
    start: "top center"
  }
});

/*---1つ目の要素---*/
custom_anime.to('.js-demo-section1', //アニメーションする要素
  { keyframes: [
    { duration: 0.5, x: 100, y:200 }, //このアニメーションが終わったら下のアニメーションが起こる
    { duration: 1, x: 300, y:400} 
  ]}
);

toggleActionsで指定できるものは以下。

  • play … アニメーションをスタートさせる
  • pause … 一時停止
  • resume … アニメーションを再開させる
  • reset … アニメーション開始直前の状態に戻す
  • restart … はじめに戻ってアニメーションを開始
  • complete … アニメーション直後の状態にする
  • reverse … アニメーションを逆再生する
  • none … 何も指定しない

どのように動くかは以下のCodePenで確認できます。

toggleActionsでは4つのタイミングでアニメーションの状態を指定できます。
以下は例です。

toggleActions: 'play pause resume reverse',

一番左から順番に説明していきます。

① onEnter (上の例ではplayの部分)
スクロール位置が「開始」を超えて下に移動したときのコールバック(通常、トリガーがスクロールされて表示されたとき)

② onLeave(上の例ではpauseの部分)
スクロール位置が「終了」を超えて下に移動したときのコールバック(通常、トリガーがスクロールして表示されなくなったとき)

③ onEnterBack(上の例ではresumeの部分)
スクロール位置が「終了」を超えて上に移動したときのコールバック(通常、トリガーがスクロールしてビューに戻ったとき)

④ onLeaveBack (上の例ではreverseの部分)
スクロール位置が「開始」を超えて上に移動したときのコールバック(通常、トリガーが開始を超えて後方にスクロールされたとき)

スマホとPC表示でアニメーションを切り替えたい

matchMediaを使うことで、デバイスごとにアニメーションを変更する設定もできます。

ScrollTrigger.matchMedia({
	
  // 960px以上
  "(min-width: 960px)": function() {
   gsap.to('.js-demo-section', { 
      autoAlpha: 1,
      x: 1000, 
      scrollTrigger: {
      trigger: '.js-trigger',
      start: 'top center', 
    }
   });
  },

  // 600px以上959px以下
  "(min-width: 600px) and (max-width: 959px)": function() {
      gsap.to('.js-demo-section', { 
      autoAlpha: 1,
      x: 800, 
      scrollTrigger: {
      trigger: '.js-trigger',
      start: 'top center', 
    }
   });
  },

  // 599px以下
  "(max-width: 599px)": function() {
      gsap.to('.js-demo-section', { 
      autoAlpha: 1,
      x: 300, 
      scrollTrigger: {
      trigger: '.js-trigger',
      start: 'top center', 
    }
   });
  },
	
  // メディアのサイズに関係なく、すべてに適用する
  "all": function() {
     gsap.set('.js-demo-section', {autoAlpha: 0});
  }

}); 

詳しくは公式サイトを参照してください。

ライセンスについて

GSAPのScrollTrigger.jsについて、mitライセンスではないですが基本的には無料で使用することができます。有料になる場合は以下です。

有料になる場合

  • 使用または参加するのに料金が必要なウェブサイトまたは製品での使用
  • 複数のエンドユーザーに販売されるゲームや製品での使用

詳しくは公式サイトを参照してください。

おわりに

今回はGSAPのScrollTrigger.jsを使ったスクロールアニメーションについて説明しました。サイト内に複雑なアニメーションをいくつも実装したい場合にオススメです。

また、CSS側でアニメーションの指定をせず、すべてScrollTrigger.jsだけで完結することも良いですね。複雑なアニメーションでも動きが重くならず、サクサク動きます。