模写修行メディア

JavaScript(jQuery)でスクロールアニメーションを自作する方法を解説!

JavaScript(jQuery)でスクロールアニメーションを自作する方法を解説!

この記事をシェア:

スクロールアニメーションを取り入れているサイト誰でも一度はみたことがあるのではないでしょうか?

簡単に実装できるライブラリは多くありますが、以下のような点で導入しづらいケースもあります。

  • アニメーションタイプが限定されている
  • カスタマイズが難しい
  • 不要なコードが含まれている
  • 複数のライブラリを使用する場合、ライブラリ同士が干渉して、正常に動作しない

自作すると自由度が高く、上のようなデメリットを回避できます。今回は、JavaScript(jQuery)を使用したスクロールアニメーションの実装方法を紹介します。

HTML・CSSを追加するだけで、アニメーションタイプを増やせたり、なるべく汎用性があるようにしました。

jQueryを使いますが、jQuery使わない場合も同じ考え方です。最後にJavaScriptのみで行うコードも記載したので、参考にしてみてください。

まずはライブラリを使って簡単に実装してみたい方は、下記の記事も参考にしてみてください!

【超簡単】JavaScriptライブラリaos.jsの使い方を解説!

単体でフェードインさせる

要素が画面下部200pxに達したタイミングで0.6秒かけて、50px下の位置から、フェードアップさせる図

シンプルなスクロールアニメーションを実装します。要素が画面下部200pxに達したタイミングで0.6秒かけて、50px下の位置から、フェードアップさせます。

  1. HTMLを記述する
  2. CSSを記述する
  3. JavaScript(jQuery)を記述する

この流れで紹介します。

HTMLを記述する

<section class="single">
    <h2>DEMO1</h2>

    <div class="single-item u-fade-type-up js-scroll-trigger">
        <img src="img/sample.jpg" alt="">
    </div>

    ...
    </div>
</section>
Point
役割ごとにクラスを分けることで、管理しやすいコードになります。

💡 class=”single-item"

レイアウトや見た目のスタイルを指定するためのクラスです。こちらのクラスには、アニメーション関連のCSSは指定しません。

💡 class=”u-fade-type-up"

アニメーション時のスタイルや動きを指定するためのクラスです。アニメーション関連のスタイルは全てこちらのクラスに指定します。「u-」は、「utility」の「u」です。

💡 class=”js-scroll-trigger"

トリガー用のクラスです。JavaScript側で要素が画面下部200pxに達したかを判定する際に必要となります。「js-」がついているクラスは、JavaScriptから操作するクラスなので、CSSの指定はしません。

CSSを記述する

/* レイアウトや見た目のスタイル */
/* ---------------------------- */

.single-item + .single-item{
    margin-top: 80px;
}

/* アニメーションスタイル */
/* ---------------------------- */

/* アニメーション前 */
.u-fade-type-up{
    transform: translateY(50px);
    opacity: 0;
}

/* トリガー発火でis-activeを付与 */
.u-fade-type-up.is-active{
    transition: .6s;
    transform: translateY(0);
    opacity: 1;
}

💡 アニメーション前

transform: translateY(50px);で要素を50px下の位置に配置、かつopacity: 0;で透明にしています。

💡 トリガー発火

要素が画面下部200pxに達したタイミングで、JavaScriptでis-activeクラスを付与します。0.6秒かけて、透明度と位置を元に戻します。

JavaScript(jQuery)を記述する

$(function () {
    // aimation呼び出し
    if ($('.js-scroll-trigger').length) {
        scrollAnimation();
    }

    // aimation関数
    function scrollAnimation() {
        $(window).scroll(function () {
            $(".js-scroll-trigger").each(function () {
                let position = $(this).offset().top,
                    scroll = $(window).scrollTop(),
                    windowHeight = $(window).height();

                if (scroll > position - windowHeight + 200) {
                    $(this).addClass('is-active');
                }
            });
        });
    }
    $(window).trigger('scroll');
});

ページ内にjs-scroll-triggerクラスが存在した場合、スクロールアニメーションの処理であるscrollAnimation関数を実行します。

少し長いので、分割して解説します。


💡 関数呼び出

// aimation呼び出し
if ($('.js-scroll-trigger').length) {
    scrollAnimation();
}

scrollAnimation関数の呼び出しを行っています。

if文の条件$('.js-scroll-trigger').lengthは、ページ内にjs-scroll-triggerクラスが存在した場合「true」となり、関数呼び出しが行われます。js-scroll-triggerクラスが存在しない場合は、呼び出されません。

js-scroll-triggerクラスの有無を確認した上で、関数を実行するか否かを判断している理由は、scrollAnimation関数内にscrollイベントが含まれているからです。scrollイベントは比較的負荷の高い処理になので、ページ内にスクロール連動アニメーションが存在した場合にのみ、処理を実行しています。


💡 scrollAnimation関数

// aimation関数
function scrollAnimation() {
    $(window).scroll(function () {
        $(".js-scroll-trigger").each(function () {
            let position = $(this).offset().top,
                scroll = $(window).scrollTop(),
                windowHeight = $(window).height();

            if (scroll > position - windowHeight + 200) {
                $(this).addClass('is-active');
            }
        });
    });
}
$(window).trigger('scroll');

長くなるので、この中のコードをさらに分割して解説します。

$(window).scroll(function (){
    …
});

スクロールに連動させるためには、常に処理に必要となる要素の位置を監視、取得しなければいけません。従って、scrollイベントを使用し、スクロール時に処理が行われるようにします。

$(".js-scroll-trigger").each(function(){
    …
});

eachメソッドを使用し、全トリガー要素(class=”js-scroll-trigger”が指定されている要素)に対して処理を適応させます。

.each()は、指定した要素や配列、オブジェクトなどに対し、ループ処理を行うメソッドです。

$(".js-scroll-trigger")に対して指定することで、サイト内の全トリガー要素に対して、以下の処理を適応させていることになります。

let position = $(this).offset().top,
    scroll = $(window).scrollTop(),
    windowHeight = $(window).height();

ここでは、「トリガーが画面下部 200px に達したか」を判定するために必要な情報を変数に格納しています。

.offset().topは、ドキュメント最上部を基準に指定要素のY座標を戻り値として返します。従って、変数positionには各トリガーの垂直方向の位置が格納されます。

scrollTop()メソッドは、垂直方向のスクロール位置を戻り値として返します。従って、$(window)に対して、.scrollTop()を指定することで、ブラウザのスクロール位置を取得することが可能となります。

$(window).height()ではウィンドウの高さを取得し、格納しています。

// 発火位置を「画面下部から200pxの位置」に設定する場合
if (scroll > position - windowHeight + 200){
    $(this).addClass('is-active');
}

// 発火位置を「画面中央」に設定する場合はこちら
if (scroll > position - windowHeight / 2){
    $(this).addClass('is-active');
}

「トリガーが画面下部 200px に達した」と判定した場合、is-activeクラスを付与しています。

今回のDEMOでは、発火位置を「画面下部 200px」に設定してますが、実装していると「画面中央」に設定したい場合も出てきます。

$(window).trigger('scroll');

リロード時などに、is-activeクラスが外れてしまうのを防いでいます。

今回は「処理に必要となる要素の位置情報を常に監視、取得し、その情報に基づいて、画面下部 200px に達したかを判定し、アニメーションを実行」しています。

しかし、仮に要素が画面下部 200px に達していた場合でも、リロードなど、再読み込みをかけてしまうと変数に格納されていた位置情報がリセットされてしまい、指定したはずのクラスが外れてしまいます。

従って、JavaScript ファイルが読み込まれた際に意図的にスクロールイベントを発生させ、各変数に再度位置情報を格納しています。

trigger()メソッドは、任意のタイミングで指定したイベントを実行することができる jQueryメソッドで、引数にはイベントを指定します。

windowに対して、.trigger('scroll')を指定することで実際にはスクロールを行なっていなくても、スクロールイベントを発生させることが可能になります。

一斉にフェードインさせる

要素が画面下部200pxに達したタイミングで複数の要素を0.4秒おき0.6秒かけて、50px下の位置から、フェードアップさせる図

今まで解説した内容は、要素単体でのフェードアップでしたが、ここでは、あるトリガーに達したら、「一斉にフェードインさせる」スクロールアニメーションを実装します。

仕様
要素が画面下部200pxに達したタイミングで複数の要素を0.4秒おきに一斉にフェードアップさせます。また、フェードアップの挙動に関しては、DEMO1同様0.6秒かけて、50px下の位置から実行します。

JavaScriptはDEMO1と全く同じです。HTML・CSS を少し変更するだけで簡単に実装できます。DEMO1と異なる箇所のみ解説していきます。

HTMLを記述する

<section class="multiple">
    <h2>DEMO2</h2>

    <div class="multiple-list js-scroll-trigger">
        <div class="multiple-item u-fade-type-up">
            <img src="img/sample.jpg" alt="">
        </div>

        ...
    </div>
</section>

トリガーが画面下部 200px に達したタイミングでトリガー内の要素を一斉フェードインさせたいので、トリガーとなる div に対してjs-scroll-triggerクラスを、フェードイン要素となるdivに対してu-fade-type-upクラスを指定します。

CSS を記述する

/* レイアウトや見た目のスタイル */
/* ---------------------------- */

.multiple-list{
    display: flex;
    justify-content: space-between;
    flex-wrap: wrap;
}

.multiple-item{
    width: 49%;
    margin-top: 20px;
}

.multiple-item:nth-child(-n+2){
    margin-top: 0;
}

/* アニメーションスタイル */
/* ---------------------------- */

/* アニメーション前 */
.u-fade-type-up{
    transform: translateY(50px);
    opacity: 0;
}

/* トリガー発火でis-activeを付与 */
.is-active .u-fade-type-up{
    transition: .6s;
    transform: translateY(0);
    opacity: 1;
}

.is-active .u-fade-type-up:nth-child(2){transition-delay: .4s;}
.is-active .u-fade-type-up:nth-child(3){transition-delay: .8s;}
.is-active .u-fade-type-up:nth-child(4){transition-delay: 1.2s;}
.is-active .u-fade-type-up:nth-child(5){transition-delay: 1.6s;}
.is-active .u-fade-type-up:nth-child(6){transition-delay: 2s;}

@media screen and (min-width: 768px) {
    .multiple-item{
        width: 32%;
    }

    .multiple-item:nth-child(-n+3){
        margin-top: 0;
    }
}

💡 アニメーションスタイル

DEMO1の単体フェードインは、フェードイン要素にis-activeがつきましたが、一斉フェードインでは、フェードイン要素ではなく、親要素のトリガーにis-activeがつきます。アニメーション後のスタイル指定は.is-active .u-fade-type-upのようになります。

各要素を0.4秒おきにフェードアップしたいので、transition-delayを利用し、遅延実行しています。1つ目の要素は、トリガーにis-activeがついたと同時に実行され、2つ目の要素は0.4秒後、3つ目の要素は0.8秒後...に実行されます。

delayは、jQuery側で指定することも可能ですが、今回はCSS側で指定しています。基本的にスクロール連動は、限られた箇所でしか利用しないので、CSS側で指定した方が良いかなと思います。

jQueryを使わずにJavaScriptのみで実装する

// トリガー取得
const scrollTrigger = document.querySelectorAll('.js-scroll-trigger');

// aimation呼び出し
if (scrollTrigger.length) {
    scrollAnimation(scrollTrigger);
}

// aimation関数
function scrollAnimation(trigger) {
    window.addEventListener('scroll', function () {
        for (var i = 0; i < trigger.length; i++) {
            let position = trigger[i].getBoundingClientRect().top,
                scroll = window.pageYOffset || document.documentElement.scrollTop,
                offset = position + scroll,
                windowHeight = window.innerHeight;

            if (scroll > offset - windowHeight + 200) {
                trigger[i].classList.add('is-active');
            }
        }
    });
}

jQueryを利用せず、ネイティブのJavaScriptで実装する際は、上記のコードに変更してください。やっていることは、jQuery版と同じです。

コードに関して気になる方は、ご自身で調べてみてください。

応用例のサンプル

応用例としてサンプルをいくつか作成しました。

サンプルデモのアニメーションはJavaScriptは今まで解説したコードで、HTML・CSSのみ追加したものになります。このようにHTML・CSSをいじるだけで、カスタマイズも出来ます。

コーディングの練習が出来るサービス「模写修行」を作りました 🎉

模写修行のトップページのスクリーンショット

「模写修行」はこんな方におすすめ!

  • 基礎学習を終えて実践的な経験を積みたい
  • 破綻しない書き方を学びたい(= CSS設計を意識したコーディングを学びたい)
  • 実務と同じようにXDのデータを見ながらコーディングの練習をしたい
  • 現役で制作をやっている人のコードを見たい

基礎学習を終えた方が次にやるべきことは、実践に近い形でたくさん練習することです。そして、わからないことがあれば、その都合調べて理解する。この繰り返しでコーディングやプログラミングは上達します。

「模写修行」ではデザインデータ(XD) / web上で見れる解説 / サンプルコードを配布しています。ご自身でデザインを見ながらコーディングに挑戦し、解説とサンプルコードで深く学ぶことができるサービスです。

この記事をシェア:

模写修行のトップページのスクリーンショット
模写修行

駆け出しエンジニアのためのコーディング練習教材