20
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

NIJIBOXAdvent Calendar 2022

Day 14

GSAPのScrollTriggerで横スクロール制御

Last updated at Posted at 2022-12-22

はじめに

世の中にはリッチで素敵なアニメーションが効いたwebサイトがたくさんあり、思わず見惚れてしまうことがありますよね...!
そんなwebサイト達を眺めていた時、ある地点までスクロールした途端に横スクロールに切り替わるアニメーションがあり、その実装方法が気になったので調べて今回軽いデモを作成してみました。

これがはじめての記事投稿で拙い内容になってしまいましたが、どなたかのお役に立てれば幸いです!

GSAPの「ScrollTrigger」とは?

GSAP(GreenSock Animation Platformの略)はGreenSock社が開発しているJavaScriptのアニメーションライブラリであり、シンプルで豊富なアニメーションを表現できるため、web制作のアニメーション実装を手軽に行うことができます。

その中のライブラリのひとつに「ScrollTrigger」があります。
これはその名の通り、ページスクロールをトリガーとして、設定したタイミングでアニメーションを実行することができるライブラリです。

導入方法

GSAPの導入方法にはnpm・ダウンロード・GitHubなど何通りかありますが、今回はそのうちのscriptタグを貼り付ける方法を選択しました。

2022/12月現在の最新のバージョンで、GSAPとScrollTriggerをそれぞれhtmlファイルのhead内に記述してみます。

HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.3/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.3/ScrollTrigger.min.js"></script>

これで、Javascript内でGSAPとScrollTriggerを利用できるようになりました!

作ったもの

今回作ったのは以下のgifのシンプルな内容です。
・アニメーションなしエリアからスクロールしていき、GSAP ScrollTriggerエリアの上端が画面上端と重なった瞬間に横スクロールに切り替わる。
・4枚の画像のスクロールが終わると、また普通の縦方向スクロールに戻る。
といった特徴があります。
※画面右端に赤文字と緑文字が見えますが、ScrollTriggerのマーカー機能を使っています。
ezgif.com-gif-maker.gif

実装

HTML
<section class="common__section">
  <div class="empty__container common__container">
    <h2 class="common__title">アニメーションなし<br>エリア</h2>
  </div>
</section>

<section class="side-scroll__section common__section">
  <div class="common__container">
    <div class="side-scroll__itemOuter">
      <div class="side-scroll__itemInner">
        <h2 class="common__title">
          GSAP ScrollTrigger<br>エリア
        </h2>
        <img class="side-scroll__item" src="https://source.unsplash.com/VDXtVYJVj7A" alt="">
        <img class="side-scroll__item" src="https://source.unsplash.com/Av90kkK6mp0" alt="">
        <img class="side-scroll__item" src="https://source.unsplash.com/QKo-op_gR9I" alt="">
        <img class="side-scroll__item" src="https://source.unsplash.com/3CJbeZr9Qjg" alt="">
      </div>
    </div>
  </div>
</section>
CSS
/* リセットCSS */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

ul,
li {
  list-style: none;
}

body {
  background-color: #A4BE7B;
  color: #FFF;
  font-family: Arial, Helvetica, sans-serif;
}

/* 共通 */
.common__section {
  padding: 100px 0;
  width: 100%;
}

.common__container {
  display: flex;
  margin: 0 auto;
  max-width: 900px;
  width: 80%;
}

.common__title {
  font-size: 40px;
  font-weight: bold;
  letter-spacing: 0.05em;
}

/* アニメーションなしエリア */
.empty__container {
  height: 900px;
}

/* GSAP ScrollTriggerエリア */
.side-scroll__section {
  background-color: #285430;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;/* はみ出た部分を隠しておく */
}

.side-scroll__itemOuter {
  position: relative;
  height: 700px;
  width: 100%;
  
}

.side-scroll__itemInner {
  position: absolute;
  top: 60px;
  left: 0;
  display: flex;
  gap: 0 60px;
}

.side-scroll__item {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-left: 80px;
  height: 400px;
  width: 600px;
  
}
JS
const itemWrapper = document.querySelector('.side-scroll__itemOuter');
const itemInner = document.querySelector('.side-scroll__itemInner');

gsap.to(itemInner, {
  x: () => -(itemInner.clientWidth - itemWrapper.clientWidth),
  ease: 'none',
  scrollTrigger: {
    trigger: '.side-scroll__section', 
    start: 'top top',
    end: () => `+=${itemInner.clientWidth - itemWrapper.clientWidth}`,
    markers: true,
    scrub: true, 
    pin: true,  
    invalidateOnRefresh: true, 
    anticipatePin: 1, 
  },
});


ではJavaScriptについて細かく見ていきましょう。 まずは、
gsap.to(第一引数, 第二引数);

と記述します。
第一引数にはターゲットとなる要素を指定、第二引数は{}で囲み、第一引数のターゲットとなる要素にどのようなアニメーションをさせるかを指定します。

JS

gsap.to(itemInner, {
  //各種設定を記述
  },
});

今回横スクロールさせたいターゲットとなる要素itemInnerに対し、{}の中でアニメーション指定を行います。

//x方向にどれくらい動かすか (ターゲットとなる要素の横幅 - ターゲットとなる要素のラッパーの横幅)
x: () => -(itemInner.clientWidth - itemWrapper.clientWidth) 

画面リサイズに対応するため関数で指定し、左移動(X軸のマイナス方向)なので横幅の値はマイナスにします。

また、ターゲットとなる要素の横幅からターゲットとなる要素のラッパーの横幅を引いていますが、これはターゲットとなる要素の右端がターゲットとなる要素のラッパーの右端と重なるタイミングまで横スクロールさせたいためです。

//イージング(加減速)は今回指定なし
ease: 'none',

イージング指定の下にscrollTrigger特有の指定を記述しています。

scrollTrigger: {
    trigger: '.side-scroll__section', 
    start: 'top top', 
    end: () => `+=${itemInner.clientWidth - itemWrapper.clientWidth}`,
    markers: true,
    scrub: true, 
    pin: true, 
    invalidateOnRefresh: true, 
    anticipatePin: 1, 

  },

こちらもそれぞれ設定の詳細を見ていきましょう。

//アニメーション発火のトリガーとなる要素を指定
trigger: '.side-scroll__section'

ここではアニメーション発火のトリガーとして、「GSAP ScrollTriggerエリア」全体を指すクラスを指定しています。

//scrollTriggerの開始位置
start: 'top top'

1つめのtopはトリガーとなる要素の部分を指します。
2つめのtopはビューポートの部分を指します。
つまり、トリガーとなる要素の上端がビューポートの上端にきたら開始という意味になります。

//scrollTriggerの終了位置
end: () => `+=${itemInner.clientWidth - itemWrapper.clientWidth}`

ターゲットとなる要素を横スクロールさせた分だけアニメーションさせたいので、xと同じ式にしています。
+=を使用する時はテキストと見做されるため、テンプレートリテラルで記述しています。
また、xと同様、画面リサイズに対応するため関数で指定しています。

//マーカー機能
markers: true

デバッグができるように、マーカー機能をtrueにしています。
この設定を行うと、アニメーション開始位置を緑文字・アニメーション終了位置を赤文字で画面上に示してくれるようになります。

//スクロールと同期
scrub: true

trueにすることでスクロールとアニメーションを同期させることができます。

//要素を画面に固定
pin: true

trueにすることでScrollTriggerが発火している間、ターゲットとなる要素を画面に固定(ピン留め)します。

//画面リサイズ時の値再計算
invalidateOnRefresh: true

trueにすることで画面リサイズ時に値の再計算を行ってくれます。今回はxendの再計算が対象となります。

//高速スクロール時画面ずれ防止
anticipatePin: 1

画面の固定(ピン留め)を行っている場合、高速スクロールしたときにピン留めのタイミングが遅れて見えることがあるため、それを防止するために数値1を設定します。通常はこの値で十分ですが、この値を小さくしたり大きくしたりして、ピン留めを行うタイミングを調整することができます。

まとめ

以上、記述範囲に限定しての内容となってしまいましたが、いかがでしたでしょうか?
個人的にはGSAPであらかじめ用意されているオプションが豊富で、あっという間に実装ができるのでもっと使いこなしていきたいなと思いました。

それでは、ここまで読んでいただきありがとうございました!!

参考

20
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?