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

### どういうこと？

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

### どういうの？

[こういうの。](http://dee909.sakura.ne.jp/qiita/201905_cssanime/)

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

### 基本的な考え方

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

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

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

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

### どうやるの？

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

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

#### 生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のコードサンプル

```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のコードサンプル

```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)のコードサンプル

```javascript

// オンロード追従のフェードインエフェクトのセッティング
$(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の発火対象となるクラス名を決める

```javascript
var effectTargetRoot  = '.effect-onload';
var effectTargetClass = ['.fadein','.fadeout','.rotate'];
```
```javascript
var effectTargetRoot  = '.effect-scroll';
var effectTargetClass = ['.fadein','.fadeout','.rotate'];
```

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

- 子要素の数に応じたループ処理を作る

```javascript
$.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);`の行では、まず `effectTargetRoot` と `value` (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を書き換える

```javascript
$(document).ready(function (){
  effectTarget.each(function(){
    $(this).addClass("active");
  });
});
```
```javascript
$(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` を付与する。

```javascript
if (scroll > targetElement - windowHeight + 200){
  $(this).addClass("active");
}
```

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

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

#### HTMLの詳細説明

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

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

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

```html
<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` がアニメーションのキモとなります。

```css
.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)
}
```

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

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