【解説版】フェードインアニメーションの基本

フェードインアニメーションはWebサイトのアニメーションの基本です。
この記事ではjQueryやライブラリなどに依存せずシンプルなコードで実装し、コードの内容を理解することで、自由にカスタマイズできるようになることが目的です!

\コピペでとりあえず実装したいという方はこちら/

コードの内容をきちんと理解したい方は読み進めてみてください。
※中級者の方はHTMLとCSSの解説は流し読みでもいいかもしれません。

目次

まずはHTMLを見てみよう

HTMLはフェードインさせたい要素すべてにベースとなるscrollanimeのクラスを付与します。

<div class="scrollanime">
  <p>動かず、ふわっと</p>
</div>

<div class="scrollanime toUp">
  <p>上に、ふわっと</p>
</div>

<div class="scrollanime toDown">
  <p>下に、ふわっと</p>
</div>

<div class="scrollanime toLeft">
  <p>左に、ふわっと</p>
</div>

<div class="scrollanime toRight">
  <p>右に、ふわっと</p>
</div>

<div class="scrollanime toBig">
  <p>大きく、ふわっと</p>
</div>

scrollanimeクラスの後に付いているもう一つのクラスはフェードインアニメーションの種類です。
今回は6種類用意しますが、この記事でコードを理解すれば自由なアニメーションを追加できるようになります。

  • scrollanimeだけ:その場で、ふわっと
  • toUp:上に、ふわっと
  • toDown:下に、ふわっと
  • toLeft:左に、ふわっと
  • toRight:右に、ふわっと
  • toBig:大きく、ふわっと

もちろん、まだこれだけでは動きません。

次にCSSを用意しよう


CSSのコード内にも簡易説明を記載していますが、下で詳しく説明していきます。
まずは、ざっと意味を想像しながら目を通してみてください。

.scrollanime {
  transition: all ease 1.5s;  /*ここで全体のアニメーション秒数を設定*/
  opacity: 0;  /*出現前は全て透明に*/
}

/*クラスに応じて出現前の位置や大きさを設定*/
.toUp {
  transform: translateY(50px);
}
.toDown {
  transform: translateY(-50px);
}
.toLeft {
  transform: translateX(50px);
}
.toRight {
  transform: translateX(-50px);
}
.toBig {
  transform: scale(0.8);
}

/*画面に入った後はJSで「fadeIn」クラスが付与され、元の位置や大きさに戻る*/
.fadeIn.scrollanime {
  opacity: 1;
}
.fadeIn.toUp {
  transform: translateY(0);
}
.fadeIn.toDown {
  transform: translateY(0);
}
.fadeIn.toLeft {
  transform: translateX(0);
}
.fadeIn.toRight {
  transform: translateX(0);
}
.fadeIn.toBig {
  transform: scale(1);
}

初期状態の指定

まず、最初の4行でscrollanimeクラスに対してアニメーションの秒数と透明になるように指定します。
アニメーション前の初期状態を最初に指定しているということですね。

続いて.toUpなどの種類別の初期状態を指定しています。

例えば.toUpの場合は「上に、ふわっと」フェードインさせたいので、
初期状態ではtransform: translateY(50px);で少し下に移動させておきます。
初期状態から元の場所の戻る動きでフェードインを演出させるという仕組みになっています。
そのほかの種類別クラスも同様に初期状態を指定しています。

fadeInクラスについて

この後で解説するJSでフェードインさせたいタイミングでfadeInクラスが付くような仕組みになります。
つまり、fadeInクラスがつくことで元の場所に戻すというCSSになっています。

例を挙げて見てみましょう。

初期状態

まずは初期状態は下記のようになります。

HTMLではscrollanime toLeftクラスだけ、
CSSによって「透明」、「右に50px移動」している状態です。

HTML
<div class="scrollanime toLeft">
  <p>左に、ふわっと</p>
</div>
実際に当たっているCSS
.scrollanime {
  transition: all ease 1.5s;
  opacity: 0;
}
.toLeft {
  transform: translateX(50px);
}

JSによってfadeInクラスが付いた後

JSによってfadeInクラスが付いたあとは下記のようになります。

HTMLにはもちろんfadeInクラスが付き、
CSSでは新たにfadeInクラスがセレクタに入っているものが当たるようになり、
opacity: 1;で透過の解除、transform: translateX(0);で元の位置に戻ります。

HTML
<div class="scrollanime toLeft fadeIn">
  <p>左に、ふわっと</p>
</div>
実際に当たっているCSS
.scrollanime {
  transition: all ease 1.5s;
  opacity: 0;
}
.toLeft {
  transform: translateX(50px);
}
.fadeIn.scrollanime {
  opacity: 1;
}
.fadeIn.toLeft {
  transform: translateX(0);
}

このようにして、fadeInクラスの付け外しでアニメーションが起こるようになっています。

それでは肝心のfadeInクラスを制御するJSを見ていきましょう。

最後にJSを用意しよう

いよいよJSでスクロールに応じてフェードインするように指定していきます。
長くて難しく見えますが、解説していくので安心してください
jQueryではない理由も最後に説明しますね!

▼完成版JSコード

(() => {
  const run = () => {
    const els = document.querySelectorAll('.scrollanime');
    if (!els.length) return;

    const update = () => {
      const y = window.pageYOffset || document.documentElement.scrollTop;
      const vh = window.innerHeight || document.documentElement.clientHeight;

      els.forEach((el) => {
        const top = el.getBoundingClientRect().top + y + 80;
        el.classList.toggle('fadeIn', y >= top - vh);
      });
    };

    const onChange = () => requestAnimationFrame(update);

    window.addEventListener('scroll', onChange, { passive: true });
    update();
  };

  document.readyState === 'loading'
    ? document.addEventListener('DOMContentLoaded', run)
    : run();
})();

解説を見る自信がない方は簡易説明のみの下記をどうぞ!

\コピペでとりあえず実装したいという方はこちら/

頑張ってコードを理解するぞー!という方は気合を入れて読み進めてみてください

JSを順番に細かく解説

少し長くなりますが、細かく解説していきます!
この記事の横にエディタでJSのコードを開いてコード全体と一緒に並べて見ると理解しやすいのでオススメです。

①基本の記述

(() => {
  中身の記述
})();

一番外側のこれは中身記述を即実行してねという書き方です。

②アニメーション全体の枠になる関数の作成

const run = () => {

runという関数を作って、この中にアニメーションに関するコードを書いていきます。
作った関数run()が呼ばれるとアニメーションが動き出すという仕組みにしておくのです。

③フェードインさせる要素の取得

const els = document.querySelectorAll('.scrollanime');

これは、HTMLの中から .scrollanime というクラスを持つ要素を全部取得しています。
ページ内にフェードインの要素は複数ある想定なので、全部取得するようにしておきます。

④余計な処理を防ぐ

if (!els.length) return;

これは、.scrollanime が1つもなければ処理をやめる、という意味です。
③で解説したels(=フェードインさせる要素を取得したもの)が存在しなければ”余計な処理はしないでね”という指示です。

⑤スクロール位置の判定をする関数

const update = () => {

update関数を作って、その中にスクロール位置の判定に関する記述をしていきます。

⑥今どれだけのスクロール位置にいるか

const y = window.pageYOffset || document.documentElement.scrollTop;

スクロール位置を取得していますが、||を使って2パターンの取得方法を書いています。
まずwindow.pageYOffsetを使い、だめならdocument.documentElement.scrollTopを使うようになっています。
環境によって取れる方法が少し違うことがあるので、保険をかけた書き方です。

⑦画面の高さを取得

const vh = window.innerHeight || document.documentElement.clientHeight;

これは今見えている画面の高さを取得しています。
上の解説と同じく、2パターンの取得方法を書いてどの環境でも取れるように保険をかけた書き方をしています。

⑧フェードインさせる要素を1つずつ確認

els.forEach((el) => {

③で解説したels(=フェードインさせる要素を取得したもの)を1つずつ順番に見るという記述です。

⑨表示タイミングを設定する

const top = el.getBoundingClientRect().top + y + 80;

これは要素を表示する位置(=タイミング)を設定するコードです。
ここが、”つまずきやすいポイント”になります…!
少し砕いて見ていきましょう。

getBoundingClientRect().top とは?

これは、その要素の上端が、今の見えている表示画面の上から何pxの位置にあるかです。
ページの上からではなく、”今見えている画面の上から”という理解が大切です。

例えば、
・要素が表示画面の一番上にあるなら「0」
・要素が表示画面の上から100px下なら「100」
・要素が表示画面の上より100px上なら「-100」
のような値になります。

なぜ + y するのか?

y⑥で解説した「今どれだけのスクロール位置にいるか」を取得したものです。
getBoundingClientRect().top は”表示画面”が基準なので、そこに今のスクロール位置(y)を足すことで、ページの上から要素が何pxなのかを計算しています。

なぜ + 80 があるのか?

これは少し遅らせてアニメーションさせるための調整値です。
要素の上端が画面に入った瞬間ではなく、要素の少し下まで画面に入ってきたタイミングでアニメーションさせています。

例えば数値を変えると、
0 に近づける → 早めに出る
80 → 少し入ってから出る
150 にする → もっと遅めに出る

⑩画面内に入ったか判定して fadeIn を付ける

el.classList.toggle('fadeIn', y >= top - vh);

これが実際にfadeInクラスを付けたり外したりする部分です。
これも少し砕いて見ていきましょう!

classList.toggle('fadeIn', 条件)とは

・条件がtrueならfadeInクラスを付ける
・条件がfalseならfadeInクラスを外す
という意味です。

y >= top - vhとは

これが条件になります。
ytop - vhよりも
・大きければtrueになり、
・小さければfalseになります。

それぞれの変数に何が入っていたか、おさらいしましょう。

y⑥で解説した「今のスクロール位置」です。
top⑨で解説した「要素を表示する位置」です。
vh⑦で解説した「今見えている画面の高さ」です。

式に当てはめると、このようになります。
「今のスクロール位置」>=「要素を表示する位置」-「今見えている画面の高さ」

ここで混乱する人が多いので、下で少し補足します。
ここを諦めずに読み解く努力をする人がコーディングに向いている人といえるかもしれません。

「今のスクロール位置」>=「要素を表示する位置」だけでいいのでは?と思われる人が多いですが、
「今のスクロール位置」というのはページの最上部から今表示されている画面の上端の位置です。

一方で、スクロールして表示される「要素を表示する位置」は画面の下端より上に来たタイミングですよね。
つまり画面の高さ分、差が生じることになるので、「今見えている画面の高さ」を引いているのです。

⑪スクロールした時に呼ぶ関数を用意

const onChange = () => requestAnimationFrame(update);

onChangeという関数を作って、update関数を呼び出す処理を書いています。
update⑤で解説したスクロール位置を判定する関数でしたね。

requestAnimationFrameは何かと言うと、
ブラウザが画面を描画するタイミングに合わせて処理を実行する仕組みです。

重い処理を直で呼ぶと、動きがカクつきやすくなるので、
update関数を今すぐ無理やり実行」ではなく、
update関数を次の描画に合わせてうまく実行してね」と頼むための関数です。

⑫スクロールされた時に実行させる指示

window.addEventListener('scroll', onChange, { passive: true });

ここまで、いろいろな関数を用意してきましたね。
ついにこれが、それらを使って”実際に動いてね”と最終指令をするコードになります。
ページがスクロールされると、⑪で解説した「スクロールした時に呼ぶ関数」onChangeを実行する設定です。

このコードの流れを簡単に説明すると下記のような動きになります。

  • scrollが起きる
  • onChangeが呼ばれる ※⑪で解説した関数
  • その中でupdate()が動く ※⑤で解説したスクロール判定の関数
  • 要素ごとにfadeInクラスが付け外しされる

{ passive: true }は「このスクロールイベントの中では、スクロールを止めるような処理はしません」という意味で、ブラウザがスクロールをよりスムーズに処理しやすくなります。

scrollが起きないと実行されない?

そんな疑問を持たれ方はセンスがいいです。
下記のコードが、ページを開いた直後でもupdate関数を実行してねと指定するコードです。

update();

⑬全てのスクリプトを実行するタイミング

さて、②で解説したアニメーション全体の枠になるrunという関数を思い出してください。
runを実行させなければ、その中にある全ての指示が動きません。
そこで最後の行に全体の枠になるrunを実行させるコードが書かれてあります。

document.readyState === 'loading'
  ? document.addEventListener('DOMContentLoaded', run)
  : run();

HTMLの読み込みが終わっているかどうかを見て、適切なタイミングでrunを実行するというコードです。

少し砕いて解説すると、
document.readyState === 'loading'はページが読み込み中かどうかを確認しています。

ページが読み込み中の場合

document.addEventListener('DOMContentLoaded', run)
HTMLの読み込みが終わったタイミングでrunを実行します。

ページの読み込みが完了している場合

run();
その場ですぐ実行します。

なぜこんな書き方?

JavaScriptの読み込み位置によっては、
まだHTML要素が作られていないタイミングでコードが動くことがあります。

そうすると .scrollanime を探しても見つからない場合があります。
なので、

  • まだHTMLができていない → 完成後に実行
  • もうできている → すぐ実行

という安全な書き方にしています。

JSの解説は以上です。
ここまでお読みいただいて、”なぜ簡単なjQueryで書かないの?”と思われた方もいらっしゃるかと思います。

jQueryで書かない理由

jQueryは私も好きですし、よく使用します。
ただ、今回のような”よくコピペで使うコード”はjsで書くことをオススメしています。
理由は、

  • jQueryを別で読み込まなくてよいので、軽くなる
  • ライブラリに依存しないため管理がしやすい
  • ライブラリのバージョンなど環境に左右されず、どんなサイトでも使える
  • jQueryはJavaScriptのライブラリなので、JavaScriptそのものの理解力はあったほうが良い

個人的な見解としては、
便利なものを利用することも必要ですが、それだけでは生き残れるエンジニアにはなれないと考えています。

最後に筆者からメッセージ

最後まで読んでくださりありがとうございます

かなり長い解説になってしまったので、最後まで丁寧にお読みいただいたアナタはきっと本気でコーディングを学ぶつもりで読んでくださったのではないでしょうか。

私は最初は趣味としてコーディングをしていましたが、仕事にするまでには長い道のりがありました。
おそらく、普通に会社員をしていた方が楽だったのではと思う時もあったほど、簡単な道のりではありませんでした。

ただ、今になって思うのは、どの道に進んでも「隣の芝は青く見えた」のではないかということです。

SNSでは在宅フリーランスでカフェとかでも仕事してキラキラして見えることもあるWeb制作ですが、本当に生業にできるほど力を付けたエンジニアやデザイナーは泥臭く努力をしているものです。

きっとこの記事を最後まで読んでくださった方は泥臭い努力ができる人だと思います。
その努力が実を結ぶように、このサイトでは志高く泥臭く頑張れる人に役立つ情報を発信していこうと思います。

今後こんなことを記事にしてほしいなどあれば自由にコメントでお知らせください

その他、感想など、コメントお待ちしています。

みんなにもシェアしてあげて!

この記事を書いた人

WEBサイトで使える“動くUI”を、コピペで実装可能にし、どこでも誰でも使えるコードに特化して発信しているフロントエンドエンジニア。
中学生で初めてコードに出会い初めてコーディングを知る。
その後、WEB制作会社のフロントエンドエンジニアとしてキャリアをスタートし、フリーランスを経て、現在はWEB制作会社の代表取締役。
コーディングによって人生を創り上げてきたからこそ、コードを通して誰かの役に立てたらという想いで、これまでの知識と経験を総動員したコードを公開中。

コメント

コメントする

目次