スクロールに応じて画像が登場するアニメーション【GSAPで簡単に】

りょーすけ
りょーすけ

画像を使うインタラクティブでお洒落なアニメーションないかな~?

GSAP MAN
GSAP MAN

インタラクティブ!?GSAPこと僕の出番だ!

今回はGSAPを用いてスクロールに応じて画像が登場するアニメーションを実装しました。ただ画像が登場するものではなくひと手間加えてお洒落に仕上げています。Codepenによるデモを用意していますのでコピペしてすぐ実装することが可能です。ぜひ最後までご覧いただけると嬉しいです

お手本

See the Pen images-appear-scroll by りょーすけ (@s_ryosuke_1242) on CodePen.

PCサイズで作成しているので右上のEdit on Codepenから全画面表示で御覧ください

少しスクロールすると画像が現れるはずです

HTML

HTMLに関して注意点はクラス名.images__gridを付与された要素が2つ存在していることです。

一つだけだと幅1260px以上のデバイスの場合に右端に大きな余白が生まれてしまうためです。
横並びにすることでデバイス幅の大きい端末でも対応しています

<div class="images">
  <div class="images__inner">
      <div class="images__flex">
          <div class="images__grid">
              <img src="https://picsum.photos/200/500" alt="">
              <img src="https://picsum.photos/300/300" alt="">
              <img src="https://picsum.photos/300/200" alt="">
              <img src="https://picsum.photos/500/500" alt="">
              <img src="https://picsum.photos/200/200" alt="">
              <img src="https://picsum.photos/200/300" alt="">
          </div>
          <div class="images__grid">
              <img src="https://picsum.photos/200/500" alt="">
              <img src="https://picsum.photos/300/300" alt="">
              <img src="https://picsum.photos/300/200" alt="">
              <img src="https://picsum.photos/500/500" alt="">
              <img src="https://picsum.photos/200/200" alt="">
              <img src="https://picsum.photos/200/300" alt="">
          </div>
      </div>
  </div>
</div>

CSS (SCSS)

次にCSSです。

*{
    padding: 0;
    margin: 0;
}
.images{
    width: calc(100vw - calc(100vw - 100%));// スクロールバーを考慮して横幅設定
    padding: 100vh 0 50vh;
    overflow: hidden;// 画像たちがはみ出た分を非表示
    &__inner{
        width: fit-content;
    }
    &__flex{
        width: fit-content;
        display: flex;
        /* animation 右側に配置*/
        transform: translateX(200px);
    }
    &__grid{
        width: fit-content;
        margin: 0 auto;
        display: grid;
        gap: 20px;
        grid-template-columns: 200px 300px 500px 200px;
        grid-template-rows: 200px 100px 200px;
        padding: 0 10px;
        img{
            display: block;
            border-radius: 20px;
            overflow: hidden;
            width: 100%;
            height: 100%;

            /* animation 予め透明&大きさ変更 */
            opacity: 0;
            visibility: hidden;
            transform: scale(0.7);
        }
        img:nth-of-type(1){
            grid-column: 1/2;
            grid-row: 1/4;
        }
        img:nth-of-type(2){
            grid-column: 2/3;
            grid-row: 1/3;
        }
        img:nth-of-type(3){
            grid-column: 2/3;
            grid-row: 3/4;
        }
        img:nth-of-type(4){
            grid-column: 3/4;
            grid-row: 1/4;
        }
        img:nth-of-type(5){
            grid-column: 4/5;
            grid-row: 1/2;
        }
        img:nth-of-type(6){
            grid-column: 4/5;
            grid-row: 2/4;
        }
    }
}

一見ただスタイルをあてているように見えますがいくつか工夫点があります。

横にはみ出した分を非表示に

まずは8行目のoverflow:hidden;です。配下の.images__grid要素が横並びに配置されているかつ画像の大きさを絶対値(px)で設定しているため中身の要素の横幅を足し合わせた分、2560px以上のデバイス幅をもつデバイスでない限り横にはみ出してしまいます。

次に6行目のwidth: calc(100vw – calc(100vw – 100%));これはスクロールバー分を考慮した横幅の設定となっています
width:100%やwidth:fit-contentだと先程説明したように当然のように画面から飛び出してしまい不要な横スクロールが発生するためwidth:100vwにするのは当然の発想です

しかしwidth:100vwと指定するとスクロールバーの横幅分だけ横スクロールできる領域が発生してしまいます
これ結構見落としがちなところです

それを回避する手段としてcalc(100vw – 100%))にてスクロールバーの幅分大きさを削っています

あらかじめ画像たちを右に配置

デモからも分かるように画像たちはある地点に到達すると右側から真ん中に戻ってくるような動きをしています

そこで予めcssのtransformプロパティのtranslateXを用いて右側に配置しておきます

GSAPのメソッド .fromToでも再現できるよね??

知識のある方は.fromToでも可能では?という疑問がでてくると思います。

可能ですが仮にJavaScriptを読み込む前にWebページが表示されてしまった場合のことを考慮してスタイルが読み込まれた時点で右に配置しておくような処理をしています

英語でいうと三人称単数ではあるけども疑問文の時、一般動詞にはsつけないルールだけどsと付けてしまったくらいの差異(例え分かりづらかったらスミマセンw)

あらかじめ画像を非表示&大きさを小さくしておく

あらかじめ画像たちを右に配置していたときと同様に状態変化の前にスタイルを適用しておきます

/* animation 予め透明&大きさ変更 */
opacity: 0;
visibility: hidden;
transform: scale(0.7);

別の記事でも説明していますがopacity:0;だけだとその要素を選択できてしまう状態になっているので完全に非表示にはできていません。そこでvisibility:hidden;を併用することでその要素分場所を確保した状態で非表示にすることが可能です。

JavaScript

次に最重要部分のJavaScriptです。アニメーションの基本単位Tweenは3つだけ
大きく分けて以下の3つの処理に分けられます

・透明な状態から大きさを変えながら出現
・トリガー位置に到達したら画像を囲む要素が元の位置に戻る
・スクロールに応じて画像を囲む要素が左に移動

window.addEventListener('DOMContentLoaded', function () {
    /* 透明な状態から大きさを変えながら出現*/
    gsap.to('.images__grid img',{autoAlpha:1,scale:1,scrollTrigger:{
            trigger:'.images__flex',
            start:'top 60%',
            toggleActions:'play none none reverse'
        }
    })
    /* トリガー位置に到達したら画像を囲む要素が元の位置に戻る */
    gsap.to('.images__flex',{x:0,scrollTrigger:{
            trigger:'.images__flex',
            start:'top 60%',
            toggleActions:'play none none reverse'
        }
    })
    /* スクロールに応じて画像を囲む要素が左に移動 */
    gsap.to('.images__inner',{x:-200,scrollTrigger:{
            trigger:'.images__flex',
            start:'top 60%',
            end:'bottom top',
            scrub:true,
            toggleActions:'play none none reverse'
        }
    })
})

透明な状態から大きさを変えながら出現

CSSのところでも説明していましたがあらかじめ要素を非表示&要素を小さくしておりましたのでメソッドは.to()で問題ありません

autoAlphaプロパティはopacityとvisibiltyを組み合わせたようなもので数値は0~1を指定するのでopacityと同じ感覚で扱います

トリガーは.images__gridを横並びにしている.images__flexで要素の一番上が画面上から60%の位置に来た時にアニメーションが発火するような指定となっています(4,5行目)

6行目のtoggleActionsはトリガー位置に下から進入するとアニメーションが逆再生(reverse)され何度もアニメーションを再生させること可能です。これは他2つのTweenも同様です

トリガー位置に到達したら画像を囲む要素が元の位置に戻る

これの大事なポイントとしては画像自体にxプロパティを使用して横移動させるのではなく画像を囲む要素に対して横移動させている点です

実際画像自体に指定するか否かでそこまで差異はないのですが処理の負荷を考慮してこのような記述にしています。
例えば画像が1000枚あったとしてそれを一つ一つ同時に動かすのは大変ですよね…
その考え方と同じで画像1000枚入ったボックスを動かしたほうが楽です(人間目線)

スクロールに応じて画像を囲む要素が左に移動

重要なところはスクロールに応じてです。ここがGSAPのプラグインScrollTriggerの威力が発揮できるところ

ポイントは21行目のscrub:true;これでスクロールに応じた動き(スクラブアニメーション)を表現できます
これを設定しているときに限らずですがScrollTriggerを使用しているときに常に意識しておきたいのはアニメーションが終わる位置です

これは20行目のend:’bottom top’; で指定しています。triggerとなる要素の一番下の部分が画面一番上に到達したときにアニメーションが終わります

まとめ

コード解説は以上となります。行数は比較的短めだと思いますが、あらかじめcssで状態変化前のスタイルを適用しておく必要やスクロールに応じたアニメーションを実装する際に必要となるScrollTriggerのプロパティなど重要なポイントがいくつかありましたね。こういったお洒落な機能はどうしても実務で使う場合はどうなるんだろうかという視点が抜けがちです。ぜひそういった目線で実装してみると一段レベルの高い重宝されるコーダーになることが可能です。ぜひこの記事が参考になれば嬉しいです。最後までご覧いただきありがとうございました!次回もお楽しみに!