5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

WEBページのロードやスクロールするとふわっと現れるアレを作る

Last updated at Posted at 2019-06-04

どういうこと?

タイトルの通りですが、最近WEBページのロードやスクロールその他の操作で、画像などの要素がふわっと現れるおしゃれな演出が増えましたよね。ああいうアニメーションエフェクトを簡単に実装するサンプルを作ってみました。

どういうの?

こういうの。

サンプル画像はフリー素材をお借りしました。どうせなら可愛い物が良いですよね、ってことで可愛い子猫たち。

基本的な考え方

  • アニメーションエフェクトを反映したい親要素
  • 実際にアニメーションエフェクトを適用したい子要素

この二つがセットで動作の基準となります。その上で、

  1. CSSにアニメーションエフェクトパターンを仕込む。
  2. HTML内のアニメーションエフェクトさせたい要素にCSSクラス名を設定する。
  3. そのアニメーションエフェクトの発火動作をJSで監視・制御する。

という流れで動作します。

どうやるの?

順を踏んで原理を簡単に説明します。

まずCSSから見ていきましょう。作るときは生CSSでも良いですがSCSSが楽で良いです。サンプルではフェードイン・アウトしながら要素が上下左右にまっすぐ動く、もしくは少し回転しながら動くアニメーションエフェクトを設定しておきました。クラス名やCSSの中身は原理を理解してからお好みに修正するのが良いと思います。ファイル名は test.css としておいてください。

生CSSのコードサンプル


@charset "utf-8";

/* Animation */

.fadein {
  -webkit-transition: all 1s;
  transition: all 1s;
  opacity: 0
}
.fadein.active {
  opacity: 1
}
.fadein.move-to-top {
  -webkit-transform: translateY(50px);
  transform: translateY(50px)
}
.fadein.move-to-top.active {
  -webkit-transform: translateY(0);
  transform: translateY(0)
}
.fadein.move-to-bottom {
  -webkit-transform: translateY(-50px);
  transform: translateY(-50px)
}
.fadein.move-to-bottom.active {
  -webkit-transform: translateY(0);
  transform: translateY(0)
}
.fadein.move-to-left {
  -webkit-transform: translateX(50px);
  transform: translateX(50px)
}
.fadein.move-to-left.active {
  -webkit-transform: translateX(0);
  transform: translateX(0)
}
.fadein.move-to-right {
  -webkit-transform: translateX(-50px);
  transform: translateX(-50px)
}
.fadein.move-to-right.active {
  -webkit-transform: translateX(0);
  transform: translateX(0)
}

.fadeout {
  -webkit-transition: all 1s;
  transition: all 1s;
  opacity: 1
}
.fadeout.active {
  opacity: 0
}
.fadeout.move-to-top {
  -webkit-transform: translateY(50px);
  transform: translateY(50px)
}
.fadeout.move-to-top.active {
  -webkit-transform: translateY(0);
  transform: translateY(0)
}
.fadeout.move-to-bottom {
  -webkit-transform: translateY(-50px);
  transform: translateY(-50px)
}
.fadeout.move-to-bottom.active {
  -webkit-transform: translateY(0);
  transform: translateY(0)
}
.fadeout.move-to-left {
  -webkit-transform: translateX(50px);
  transform: translateX(50px)
}
.fadeout.move-to-left.active {
  -webkit-transform: translateX(0);
  transform: translateX(0)
}
.fadeout.move-to-right {
  -webkit-transform: translateX(-50px);
  transform: translateX(-50px)
}
.fadeout.move-to-right.active {
  -webkit-transform: translateX(0);
  transform: translateX(0)
}

.rotate {
  -webkit-transition: all 1s;
  transition: all 1s;
  opacity: 0
}
.rotate.active {
  -webkit-transform: rotate(0) scale(1, 1);
  transform: rotate(0) scale(1, 1);
  opacity: 1
}
.rotate.move-to-top {
  -webkit-transform: translateY(500px) rotate(-45deg) scale(0.5, 0.5);
  transform: translateY(500px) rotate(-45deg) scale(0.5, 0.5)
}
.rotate.move-to-top.active {
  -webkit-transform: translateY(0);
  transform: translateY(0)
}
.rotate.move-to-bottom {
  -webkit-transform: translateY(-500px) rotate(-45deg) scale(0.5, 0.5);
  transform: translateY(-500px) rotate(-45deg) scale(0.5, 0.5)
}
.rotate.move-to-bottom.active {
  -webkit-transform: translateY(0);
  transform: translateY(0)
}
.rotate.move-to-left {
  -webkit-transform: translateX(500px) rotate(45deg) scale(0.5, 0.5);
  transform: translateX(500px) rotate(45deg) scale(0.5, 0.5)
}
.rotate.move-to-left.active {
  -webkit-transform: translateX(0);
  transform: translateX(0)
}
.rotate.move-to-right {
  -webkit-transform: translateX(-500px) rotate(-45deg) scale(0.5, 0.5);
  transform: translateX(-500px) rotate(-45deg) scale(0.5, 0.5)
}
.rotate.move-to-right.active {
  -webkit-transform: translateX(0);
  transform: translateX(0)
}

SCSSのコードサンプル


@charset "utf-8";

/* Animation */

.fadein {
  transition: all 1s;
  opacity: 0;
  &.active {
    opacity: 1;
  }
  &.move-to-top {
    transform: translateY(50px);
    &.active {
      transform: translateY(0);
    }
  }
  &.move-to-bottom {
    transform: translateY(-50px);
    &.active {
      transform: translateY(0);
    }
  }
  &.move-to-left {
    transform: translateX(50px);
    &.active {
      transform: translateX(0);
    }
  }
  &.move-to-right {
    transform: translateX(-50px);
    &.active {
      transform: translateX(0);
    }
  }
}
  
.fadeout {
  transition: all 1s;
  opacity: 1;
  &.active {
    opacity: 0;
  }
  &.move-to-top {
    transform: translateY(50px);
    &.active {
      transform: translateY(0);
    }
  }
  &.move-to-bottom {
    transform: translateY(-50px);
    &.active {
      transform: translateY(0);
    }
  }
  &.move-to-left {
    transform: translateX(50px);
    &.active {
      transform: translateX(0);
    }
  }
  &.move-to-right {
    transform: translateX(-50px);
    &.active {
      transform: translateX(0);
    }
  }
}
  
.rotate {
  transition: all 1s;
  opacity: 0;
  &.active {
    transform: rotate(0) scale(1,1);
    opacity: 1;
  }
  &.move-to-top {
    transform: translateY(500px) rotate(45deg) scale(0.5,0.5);
    &.active {
      transform: translateY(0);
    }
  }
  &.move-to-bottom {
    transform: translateY(-500px) rotate(-45deg) scale(0.5,0.5);
    &.active {
      transform: translateY(0);
    }
  }
  &.move-to-left {
    transform: translateX(500px) rotate(45deg) scale(0.5,0.5);
    &.active {
      transform: translateX(0);
    }
  }
  &.move-to-right {
    transform: translateX(-500px) rotate(-45deg) scale(0.5,0.5);
    &.active {
      transform: translateX(0);
    }
  }
}

次にHTMLです。先ほど作ったCSSのクラス名を親要素と子要素に設定するだけなので非常に簡単です。 head タグ内でCSS読み込み、 body 閉じタグの直前にJQueryとJS読み込みを必ず設定してください。

HTMLのコードサンプル


<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="description" content="">
    <meta name="keywords" content="">
    <title>test</title>
    <link rel="stylesheet" href="test.css">
  </head>
  <body>
    <main>
      <div class="mv-box effect-onload">
        <img src="cat1.jpg" class="fadein">
      </div>
      <div class="section-box effect-scroll">
        <img src="cat2.jpg" class="rotate move-to-bottom">
      </div>
      <div class="column-box effect-scroll">
        <img src="cat3.jpg" class="fadein move-to-right">
        <img src="cat3.jpg" class="fadein move-to-top">
        <img src="cat3.jpg" class="fadein move-to-bottom">
        <img src="cat3.jpg" class="fadein move-to-left">
      </div>
    </main>
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <script src="test.js"></script>
  </body>
</html>

最後にJavaScript(JQuery)。WEBページのオンロードとスクロールイベントを取ります。これによりWEBサイトがインタラクティブな感じになります。(インタラクティブって言葉、かっこいいですよね) 動作原理がわかったら変数に設定されているCSSクラス名を変えちゃっても大丈夫です。ファイル名は「test.js」としておいてください。

JavaScript(JQuery)のコードサンプル


// オンロード追従のフェードインエフェクトのセッティング
$(function(){
  var effectTargetRoot  = '.effect-onload';
  var effectTargetClass = ['.fadein','.fadeout','.rotate'];
  $.each(effectTargetClass, function(i, value) {
    var effectTarget = $(effectTargetRoot + ' ' + value);
    //console.log('effectTarget= ' + i + ':' + effectTargetRoot + ' ' + value);
    $(document).ready(function (){
      effectTarget.each(function(){
        $(this).addClass("active");
      });
    });
  });
});

// スクロール追従のフェードインエフェクトのセッティング
$(function(){
  var effectTargetRoot  = '.effect-scroll';
  var effectTargetClass = ['.fadein','.fadeout','.rotate'];
  $.each(effectTargetClass, function(i, value) {
    var effectTarget = $(effectTargetRoot + ' ' + value);
    //console.log('effectTarget= ' + i + ':' + effectTargetRoot + ' ' + value);
    $(window).scroll(function (){
      effectTarget.each(function(){
        var targetElement = $(this).offset().top;
        var scroll = $(window).scrollTop();
        var windowHeight = $(window).height();
        if (scroll > targetElement - windowHeight + 200){
          $(this).addClass("active");
        }
      });
    });
  });
});

いかがでしょう?とりあえず動作しましたか?次は動作原理を説明します。動作原理に関しましてはJS→HTML→CSSの逆順が理解しやすいと思いますので、その流れで説明します。

JavaScript(JQuery)の詳細説明

  • JSの発火対象となるクラス名を決める
var effectTargetRoot  = '.effect-onload';
var effectTargetClass = ['.fadein','.fadeout','.rotate'];
var effectTargetRoot  = '.effect-scroll';
var effectTargetClass = ['.fadein','.fadeout','.rotate'];

まずこの部分です。 次で説明するセレクタの元となる部分を作ります。変数effectTargetRoot には親要素のクラス名を一つ設定し、 配列effectTargetClass には子要素のクラス名を複数設定します。どちらも配列でも良いのですが、現状では意味がないので子要素側だけ配列にしています。

  • 子要素の数に応じたループ処理を作る
$.each(effectTargetClass, function(i, value) {
  var effectTarget = $(effectTargetRoot + ' ' + value);
  //console.log('effectTarget= ' + i + ':' + effectTargetRoot + ' ' + value);
});

次にこの部分です。$.each( 条件 ){ コード }); で囲まれた部分はループ処理されます。配列 effectTargetClass に入っている文字列の個数の分だけ繰り返して実行します。配列に文字列が3個入っていると3回繰り返します。繰り返される際に、配列 effectTargetClass の中身が変数 value に格納され、ループするたびに順次、バケツリレーでその中身が入れ替わっていきます。

var effectTarget = $(effectTargetRoot + ' ' + value);の行では、まず effectTargetRootvalue (effectTargetClass)の文字列を結合し、3つのセレクタの文字列を作ります。そして出来上がったセレクタの文字列を元に【セレクタの対象となるDOM(HTML要素の構造)の内容を取得して、変数effectTargetに格納してくれ】とJQueryに命令しています。つまりは「HTML側でこのクラス名が設定されたDOM」がJSが発火するターゲットとなります。

次のコメントアウトしている //console.log('effectTarget= ' + i + ':' + effectTargetRoot + ' ' + value); の行は、WEBブラウザのデバックコンソールに文字列を出力する関数です。プログラム中で何が起こっているか確認しやすいです。

effectTarget= 0: .effect-scroll .fadein
effectTarget= 1: .effect-scroll .fadeout
effectTarget= 2: .effect-scroll .rotate

コメントアウトを外すと、今回の場合は、デバックコンソールへの出力が上記のようになっているはずです。ループは0からカウントするので、1,2,3…ではなく0,1,2…と増えます。なっていない場合は何かミスっていますので見直しましょう。動作に問題がなくなった場合(=完成した場合)は不要なのでコメントアウトしておきましょう。デバッグコンソールはChromeの場合、右クリック→「検証」か、「表示」→「開発/管理」→「デベロッパーツール」で表示できます。

  • イベントを監視してHTMLを書き換える
$(document).ready(function (){
  effectTarget.each(function(){
    $(this).addClass("active");
  });
});
$(window).scroll(function (){
  effectTarget.each(function(){
    var targetElement = $(this).offset().top;
    var scroll = $(window).scrollTop();
    var windowHeight = $(window).height();
    if (scroll > targetElement - windowHeight + 200){
      $(this).addClass("active");
    }
  });
});

更に深層部に進みます。こういう階層構造を「ネスト」と呼びます。上と下で二つのネストの内容が大きく異なるように見えますが、そうでもないです。順を追って読んでいけば怖いことはありません。

まず上の方。オンロードに関してですが、こちらはシンプルです。「ブラウザを監視してWEBページのロードが完了したらネストの中身を実行しろ」という内容です。
下の方はスクロールイベントを監視します。「ブラウザを監視してページがスクロールされたらネストの中身を実行しろ」という内容です。

ネストの中身は最終的にはどちらも、先ほどの変数 effectTarget で指定されたDOMに対してクラス名 .active を付与する動作を行います。つまりJSによってHTMLがリアルタイムに書き換えられるわけです。それが $(this).addClass("active"); の行です。例えば .effect-onload .fadein.active が付与されると .effect-onload .fadein .active になります。これにより、.active の有無でCSSのスタイルが変化し、アニメーションが実現できるわけです。

スクロールイベントの方が少し複雑ですが、下記のステップで動作します。

  1. var targetElement = $(this).offset().top; でブラウザの一番上(0px)から指定DOMまで何px離れているか取得して変数に格納する。
  2. var scroll = $(window).scrollTop(); でブラウザの一番上(0px)から現在の位置まで何pxスクロールしたのか取得して変数に格納する。
  3. var windowHeight = $(window).height(); であなたが今開いているブラウザのウィンドウサイズの高さが何pxなのか取得して変数に格納する。
  4. 座標を計算し、要求を満たした時にネストの中身を実行して、クラス名 .active を付与する。
if (scroll > targetElement - windowHeight + 200){
  $(this).addClass("active");
}

座標計算について意訳すると【指定DOMがブラウザの表示領域に現れたらネストの中身を実行する】という動作になります。 200 の部分はマージンとなるpx数です。多いようなら 0 に減らしたり、少ないようなら 500 などお好みに合わせて増やしても大丈夫です。

「クリックした時」「マウスホバーした時」などさまざまなイベントを監視して発火条件にすることもできます。JSについてはここまでで終わりです。

HTMLの詳細説明

次はHTMLですが、こちらはシンプルです。どの部分をアニメーションエフェクトの対象とするか設定するだけです。

  1. アニメーションエフェクトを反映したい親要素にクラス名 .effect-scroll を付与する。
  2. 実際にアニメーションエフェクトを適用したい子要素たちに .fadein .move-to-right 等を付与する。

結果こうなります。やることはこれだけです。

<div class="column-box effect-scroll">
  <img src="cat3.jpg" class="fadein move-to-right">
  <img src="cat3.jpg" class="fadein move-to-top">
  <img src="cat3.jpg" class="fadein move-to-bottom">
  <img src="cat3.jpg" class="fadein move-to-left">
</div>

ページがスクロールされ .column-box .effect-scroll のDOMの一番上の部分がブラウザ内に表示された時に、JSによって子要素すべてに .active が付与されます。(=HTMLが書き換わります)

CSSの詳細説明

最後にCSSですが「実際にどういったアニメーションエフェクトを定義するか」が主軸となります。

  1. transition: all 1s; は「全てのスタイルの変化をトランジションの対象とし、1s(1秒)かけてなめらかに変化する」という指定です。秒は小数点指定で値が少ないほど早くなります。
  2. opacity: 0 は透過率の指定です。0で完全な透明、1なら完全な不透明です。小数点指定できます。フェードインさせるためにスタート時は透明状態から開始したいので0を指定します。
  3. JSによりクラス名 .fadein にクラス名 .active が付与された時に opacity: 1 にトランジションします。つまり透明状態からゆっくりと不透明状態に変わる(=フェードイン)します。
  4. クラス名 .fadein に加えてクラス名 .move-to-top を付与した場合、transform: translateY(50px) により要素のY座標を本来の表示位置から 50px 下にずらす効果が追加されます。JSにより .active が付与された時に本来の表示位置である 0px までトランジションします。

結果こうなります。 transition opacity transform がアニメーションのキモとなります。

.fadein {
  -webkit-transition: all 1s;
  transition: all 1s;
  opacity: 0
}
.fadein.active {
  opacity: 1
}
.fadein.move-to-top {
  -webkit-transform: translateY(50px);
  transform: translateY(50px)
}
.fadein.move-to-top.active {
  -webkit-transform: translateY(0);
  transform: translateY(0)
}

「だんだん加速する」「だんだん減速する」などトランジションのカーブに変化をつけたりもできます。回転や拡大縮小などのトランスフォームも指定できます。色々試してみてください。

というわけで以上です。インタラクティブでかっこよく、楽しいエフェクトアニメーションライフをお過ごしください!

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?