スクロールして画面内に要素が入ったらアニメーションさせる【CSS,JavaScript】

CSSでアニメーションをつけている時に、スクロールして要素が画面に入ったら開始させたかったので備忘録です。

Demo

Demoはこちらに残しました。うまく見れない場合は右上の「EDIT ON CODEPEN」をクリックしてサイトを開いてみてください。簡易的ですが落ち葉にしてみました。画面内を少しスクロールするとアニメーションが始まります。

See the Pen animation-fall by すずのん|母でもWebクリエイター (@suzunon11) on CodePen.

  • スクロールしてアニメーションさせたい(ターゲット)要素が画面内に入ったらclassをつける
  • そのclassにCSSでアニメーションのスタイルを書く

という風にやっていきます。

HTML

Demoの落ち葉1枚分のhtml

アニメーションさせたい要素がたくさんあるとわかりにくいので、落ち葉1枚分での説明です。

HTML

<div class="box-wrapper">
    <div class="box js-scroll-fall">
      <div class="item"></div>
    </div>
  </div>

アニメーションさせたい箇所に特定のclass(例ではjs-scroll-fall)をつけます。Demoでは落ち葉がitem、落ち葉を一括りにしたものがboxで、boxが画面内に入るとアニメーションを開始するようにしたので、boxjs-scroll-fallをつけました。特定のclass名は任意で決めています。(js-scroll-animationとかでも)

JavaScript

ターゲット(アニメーションさせたい)要素を取得する

JavaScript

let targets = document.querySelectorAll('.js-scroll-fall');//ターゲット要素を取得
for (let target of targets) {
  target.classList.add('is-animated');//is-animatedクラスを加える
}

1行目:アニメーションさせたい箇所が複数あっても可能なように、アニメーションさせたい箇所につけたclass(例ではjs-scroll-fall)を、querySelectorAll()を使って複数要素取得をします。

取得したターゲット(アニメーションさせたい)要素にアニメーション用のクラスをつける

2行目:for ( … of ) 文で反復処理をします。ターゲット(js-scroll-fallクラスを持った要素)がある分、アニメーション用のクラス(例ではis-animatedクラス)をつける処理を繰り返します。

「MDN Web Docs」のfor…of構文、DOM コレクションでの反復処理について

for (variable of iterable) {

statement

}

for…of構文:https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/for…of#syntax
DOM コレクションでの反復処理:https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/for…of#iterating_over_a_dom_collection

スクロールイベントを実行する

JavaScript

window.addEventListener('scroll', function () {
//スクロールした時の処理を書く
});

スクロールイベントの記述です。この中に「アニメーションさせたい(ターゲット)要素が画面内に入ったら」の条件を記述していきます。

スクロール量、画面の高さ、ターゲット(アニメーションさせたい)要素の位置を取得する

var scroll = window.scrollY; //スクロール量を取得
var windowHeight = window.innerHeight; //画面の高さを取得
var targetPos = target.getBoundingClientRect().top + scroll; //ターゲット要素の位置を取得

window.scrollY:document(html)の上端を起点とした垂直方向のスクロールをピクセル数で取得します。

window.innerHeight:現在のブラウザ表示領域の高さを取得します。

getBoundingClientRect():ブラウザ表示領域の左上を起点としたターゲット要素の位置を示します。target.getBoundingClientRect().topでターゲット要素の上端がブラウザ表示領域の上端にきたときの値になります。これにscrollを足すとdocument(html)からのターゲット要素の位置が取得できます。

 if (scroll > targetPos - windowHeight) { //スクロール量 > ターゲット要素の位置
      target.classList.add('is-animated');//is-animatedクラスを加える
    }

アニメーションさせたい(ターゲット)要素が画面内に入ったらというのは「スクロール量がターゲット位置を超えたら」という条件で、

スクロール量 > ターゲット要素の位置

と考えてif文にして、is-animatedクラスを加える記述を入れます。

ターゲット要素の位置はtargetPoswindowHeightとなっていますが、これはtargetPos(documentからのターゲット要素の位置)からwindowHeight(ブラウザ表示領域の高さ)を引くとターゲット要素が画面内に入った分のスクロール位置が計算できるからです。スクロールの位置がターゲット要素の位置を超えてターゲット要素が画面に表示される状態であればis-animatedクラスを加える、ということになります。

以上をまとめた記述

これらをまとめて下記のように記述します。

JavaScript

let targets = document.querySelectorAll('.js-scroll-fall'); //アニメーションさせたい要素
//スクロールイベント
window.addEventListener('scroll', function () {
  var scroll = window.scrollY; //スクロール量を取得
  var windowHeight = window.innerHeight; //画面の高さを取得
  for (let target of targets) { //ターゲット要素がある分、アニメーション用のクラスをつける処理を繰り返す
    var targetPos = target.getBoundingClientRect().top + scroll; //ターゲット要素の位置を取得
    if (scroll > targetPos - windowHeight) { //スクロール量 > ターゲット要素の位置
      target.classList.add('is-animated'); //is-animatedクラスを加える
    }
  }
});

CSS

Demoの落ち葉につけているスタイル

CSS

.box-wrapper {
  position: relative;
  max-width: 600px;
  height: 100vh;
  margin: 0 auto;
  text-align: center;
}

.box {
  width: 100%;
}

/* 落ち葉 */
.item {
  position: absolute;
  left: 50%;
  transform: translate(-50%, 0);
  width: 15px;
  height: 20px;
  margin: 0 auto;
  border-radius: 0% 70%;
  background: #914928;
  opacity: 0;
}

今回はboxを括っているwrapperを100vhとして画面いっぱいの高さを設けています。落ち葉はopacity: 0;にしてアニメーション開始前は見えないようにしています。

アニメーションに関わるスタイル

CSS

@keyframes fall {
  from {
    opacity: 0;
    top: -50px;
  }

  20% {
    opacity: 1;
  }

  to {
    opacity: 1;
    top: 100vh;
  }
}

@keyframes sway1 {
  from {
    transform: translate3d(0px, 0px, 0px) rotate(0deg);
  }

  25% {
    transform: translate3d(-50px, 0px, 0px) rotate(-10deg);
  }

  75% {
    transform: translate3d(50px, 0px, 0px) rotate(10deg);
  }

  to {
    transform: translate3d(0px, 0px, 0px) rotate(0deg);
  }
}

@keyframesを使ってfallsway1という2つのアニメーションを用意します。

  • fall落ち葉が上から下に落ちていく動作を作っています。

opacity: 0;からopacity: 1;で表示されるようにし、top: -50px;→ top: 100vh;は画面外(-50px)から画面内の一番下(100vh)へ位置を動かしています。

  • sway1落ち葉が左右に揺れる動作を作っています。

translate3d(…px, 0px, 0px)でX軸方向(横)に落ち葉を少しずらしつつ、rotate(…deg)で角度をつけて傾けています。配分はお好みだと思います。

sway1だけ説明していますが、左右に揺れるパターンをもっとランダムに作りたいならsway2sway3…と用意します。

is-animatedクラスがついた時に開始するアニメーションのスタイル

CSS

.js-scroll-fall.is-animated .item {
  animation: fall 4s linear forwards, sway1 4s ease-in-out alternate;
  animation-delay: 0.3s;/* アニメーション遅延で個々のアニメーション開始をずらしたい場合、数値を変えてつける */
}

  • fall 4s linear forwards

作成したアニメーション「fall」を4秒かけlinerというタイミングで実行し、実行後はそのまま最後のkeyframesで設定されたスタイルを維持する(forwards)、という指示です。

  • sway1 4s ease-in-out alternate

作成したアニメーション「sway1」を4秒かけease-in-outというタイミングで実行し、デフォルト一方向だけの動きを毎回反転させる(alternate)、という指示です。※alternateがわかりにくかったら一度alternateの記述を外してアニメーションさせてみるといいと思います。

以上をまとめた記述

CSS

.box-wrapper {
  position: relative;
  max-width: 600px;
  height: 100vh;
  margin: 0 auto;
  text-align: center;
}

.text {
  padding: 20px;
}

.box {
  width: 100%;
}

/* 落ち葉 */
.item {
  position: absolute;
  left: 50%;
  transform: translate(-50%, 0);
  width: 15px;
  height: 20px;
  margin: 0 auto;
  border-radius: 0% 70%;
  background: #914928;
  opacity: 0;
}

@keyframes fall {
  from {
    opacity: 0;
    top: -50px;
  }

  20% {
    opacity: 1;
  }

  to {
    opacity: 1;
    top: 100vh;
  }
}

@keyframes sway1 {
  from {
    transform: translate3d(0px, 0px, 0px) rotate(0deg);
  }

  25% {
    transform: translate3d(-50px, 0px, 0px) rotate(-10deg);
  }

  75% {
    transform: translate3d(50px, 0px, 0px) rotate(10deg);
  }

  to {
    transform: translate3d(0px, 0px, 0px) rotate(0deg);
  }
}

.js-scroll-fall.is-animated .item {
  animation: fall 4s linear forwards, sway1 4s ease-in-out alternate;
  animation-delay: 0.3s;/* アニメーション遅延で個々のアニメーション開始をずらしたい場合、数値を変えてつける */
}

まとめ

これらを合わせた落ち葉1枚分のDemoはこんな感じになります。

See the Pen sample by すずのん|母でもWebクリエイター (@suzunon11) on CodePen.

今回の落ち葉のようにたくさん動かす対象があったら@keyframesで何パターンか動きを作ってつけてあげるとランダム感が出ます。

以上がCSSとJavaScriptと使った、スクロールして画面内に要素が入ったらアニメーションをさせる方法です。