スクロール時に要素がふわっと表示される動きをJavaScriptで実装してみる(備忘録)

今回はスクロールした時に要素がふわっと表示される動きをJavaScriptで実装していく。

最近のページを見ていると、スクロール時に見えない要素がいろんな角度からふわっと表示されるものを見かけることがあると思う。

これに近いものを、今回は、上から、右から、左から、下から、同じ場所からの5パターンを用意した。

この方向については、CSS側で調整できるようになっているので、色々いじって試してみてほしい。

JavaScript側ではイベント実行のタイミングや、クラスの付与・削除のコントロールをしているので、一番上までスクロールすると、また要素がふわっと表示されるようになっている。

それでは実装に入っていく。

HTMLの記述について

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>スクロール時に要素をふわっと表示|freefuntimes – 自由で楽しいひとときを…</title>
  <link rel="stylesheet" href="../reset.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main>
    <section>
      <div class="ly_mainvisual">
        <img src="img_mainvisual.jpg" alt="MV">
      </div>
    </section>
    <section>
      <div class="ly_inner" id="menu1">
        <h2 class="js_fadeScroll">下からふわっと表示</h2>
        <div class="md_textblock js_fadeScroll">
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
        </div>
      </div>
    </section>
    <section>
      <div class="ly_inner" id="menu2">
        <h2 class="js_fadeScroll">右からふわっと表示</h2>
        <div class="md_textblock js_fadeScroll">
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
        </div>
      </div>
    </section>
    <section>
      <div class="ly_inner" id="menu3">
        <h2 class="js_fadeScroll">左からふわっと表示</h2>
        <div class="md_textblock js_fadeScroll">
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
        </div>
      </div>
    </section>
    <section>
      <div class="ly_inner" id="menu4">
        <h2 class="js_fadeScroll">上からふわっと表示</h2>
        <div class="md_textblock js_fadeScroll">
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
        </div>
      </div>
    </section>
    <section>
      <div class="ly_inner" id="menu5">
        <h2 class="js_fadeScroll">同じ場所からふわっと表示</h2>
        <div class="md_textblock js_fadeScroll">
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
          <p>テキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入りますテキストが入ります</p>
        </div>
      </div>
    </section>
  </main>
  <script src="style.js"></script>
</body>
</html>

HTMLは特に難しいことはしていない。js_fadeScrollというクラスを、スクロール時にふわっと表示させたい部分に掲載しているだけだ。

CSSの記述について

@charset "utf-8";

/* ==========================
  初期設定
========================== */
*,
*::before,
*::after {
  box-sizing: border-box;
}
img {
  width: 100%;
}

/* ==========================
  ふわっと表示
========================== */
.js_fadeScroll {
  opacity: 0;
  visibility: hidden;
  transition: .7s;
}
.js_fadeScroll.js_active {
  opacity: 1;
  visibility: visible;
}
/* 下から */
#menu1 .js_fadeScroll {
  transform: translateY(30px);
}
/* 右から */
#menu2 .js_fadeScroll {
  transform: translateX(30px);
}
/* 左から */
#menu3 .js_fadeScroll {
  transform: translateX(-30px);
}
/* 上から */
#menu4 .js_fadeScroll {
  transform: translateY(-30px);
}

#menu1 .js_fadeScroll.js_active,
#menu2 .js_fadeScroll.js_active,
#menu3 .js_fadeScroll.js_active,
#menu4 .js_fadeScroll.js_active {
  transform: translate(0);
}

/* ==========================
  コンテンツの中身
========================== */
.ly_inner {
  width: 100%;
  max-width: 1080px;
  margin: 100px auto;
  padding: 20px;
  background-color: #ccc;
}
.ly_inner h2 {
  font-size: 150%;
  font-weight: bold;
  margin-bottom: 30px;
}
.md_textblock > * + * {
  margin-top: 10px;
}

CSSもいたって単純な対応であるが解説はしていく。

.js_fadeScroll {
  opacity: 0;
  visibility: hidden;
  transition: .7s;
}

こちらで、opacityを0に、visibilityをhiddenにして要素が見えないようにしている。さらに、transitionを設定しているのは、要素が0.7秒かけてふわっと表示されるようにするためである。

.js_fadeScroll.js_active {
  opacity: 1;
  visibility: visible;
}

JavaScript側でスクロール時にjs_fadeScrollの要素に対してjs_activeのクラスが付与されるようになっているため、このクラスが付与されたときに、opacityを1、visibilityをvisibleになるように設定した。

/* 下から */
#menu1 .js_fadeScroll {
  transform: translateY(30px);
}
/* 右から */
#menu2 .js_fadeScroll {
  transform: translateX(30px);
}
/* 左から */
#menu3 .js_fadeScroll {
  transform: translateX(-30px);
}
/* 上から */
#menu4 .js_fadeScroll {
  transform: translateY(-30px);
}

#menu1 .js_fadeScroll.js_active,
#menu2 .js_fadeScroll.js_active,
#menu3 .js_fadeScroll.js_active,
#menu4 .js_fadeScroll.js_active {
  transform: translate(0);
}

あとはtransformを用いて、ずらす前の位置をtranslateで決めておけばOKだ。

なお、今回はid名により指定を分けているが、他のやり方でも対応できる。CSS側でなくてもJavaScript側で対応する方法もある。人によって方法はさまざまなので興味のある方はいろんなやり方を試してみてほしい。

JavaScriptの記述について

'use strict';

// スクロールでふわっと表示
window.addEventListener('scroll',() => {
  const targetElement = document.querySelectorAll('.js_fadeScroll');
  const scrollY = window.pageYOffset;
  const windowH = window.innerHeight;
  const bodyHeight = document.body.clientHeight;
  for(let i = 0; i < targetElement.length; i++) {
    const elemClientRect = targetElement[i].getBoundingClientRect();
    const elemY = scrollY + elemClientRect.top;
    if(scrollY > elemY - windowH + 200) {
      targetElement[i].classList.add('js_active');
    } else if (bodyHeight - windowH <= scrollY) {
      targetElement[i].classList.add('js_active');
    } else {
      targetElement[i].classList.remove('js_active');
    }
  }
});

まずはスクロールした時のイベント処理を下記で記述する。IE対応は考えてないため、アロー関数で記述している。

window.addEventListener('scroll',() => {
	// この中にいっぱい処理を書いていく
});

続いてconstで変数を定義する。定義しなくても対応可能だが、後から何度も記述する手間を省くために定義しておくイメージ。

  const targetElement = document.querySelectorAll('.js_fadeScroll');
  const scrollY = window.pageYOffset;
  const windowH = window.innerHeight;
  const bodyHeight = document.body.clientHeight;
  • targetElement:HTML側でjs_fadeScrollのクラスを設定した箇所を定義
  • scrollY:window.pageYOffsetでページの上端からのスクロール量を定義
  • windowH:window.innerHeightでウィンドウズの内部の高さを定義
  • bodyHeight:document.body.clientHeightでドキュメント(body)の高さを定義
    ※targetElement、scrollY、windowH、bodyHeightは任意の名前でOK。

続いてfor文で繰り返し処理を書いていく。先ほど指定したtargetElementに対して繰り返し処理を行う。

  for(let i = 0; i < targetElement.length; i++) {
    const elemClientRect = targetElement[i].getBoundingClientRect();
    const elemY = scrollY + elemClientRect.top;
    if(scrollY > elemY - windowH + 200) {
      targetElement[i].classList.add('js_active');
    } else if (bodyHeight - windowH <= scrollY) {
      targetElement[i].classList.add('js_active');
    } else {
      targetElement[i].classList.remove('js_active');
    }
  }

繰り返し処理の中で、constで変数を定義する。

  • elemClientRect:targetElement[i]でtargetElementの数だけ処理が繰り返され、getBoundingClientRectで画面左上を基準にターゲット要素の位置を定義
  • elemY:先ほど定義したscrollYと、elemClientRect.topで画面上部からターゲットまでの距離について、足した分を定義

if文でscrollYが、「elemY – windowH + 200」よりも大きいとき、targetElementにjs_activeのクラスが付与される。もっと簡単に言うと、ウィンドウ画面内にターゲット要素が200px見えた状態になると、イベントが発火する仕組みだ。なお、この200という数値は、別の数値に調整することも可能だ。

続いてelse ifは、「bodyHeight – windowH」がscrollY以下の場合に、js_activeのクラスが付与されるようにしている。これは先ほどの200という数値が大きい場合に、最後のコンテンツに適用されない場合があるため記述している。

最後のelse文は、一番下までスクロールしてから上部にスクロールを戻していくと、js_activeのクラスが外れるようになっている。これにより再度下までスクロールした時に、ふわっと表示のアニメーションが再現されるようになる。

この記述がなければ、ふわっと表示のアニメーションが一度だけ起こる演出になるので、内容に応じて使い分けるのがいいだろう。

else {
  targetElement[i].classList.remove('js_active');
}

以上で実装完了だ。