スクロールアニメーションに必要なあれこれ

#概要
ウィンドウのスクロール量に応じて、表示・非表示を切り替えたり、マイクロインタラクションを入れたり、、などの実装をすることがよくありますが、`scrollTop()`や`offset().top`などが、毎回ごっちゃになるので、jQuery/JavaScriptでの各パラメータの取得方法をまとめました。

#基本的な考え方

##各パラメータの取得

ある要素が、スクロールによって、表示領域に入り、その後、表示領域から見えなくなるまでの動きを考えます。
図で表すと下記のようなイメージになります。
![記事用 (3).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253713/8ad7dde3-e385-b390-26ec-e90342e3401f.jpeg)
図で示したパラメータの取得方法をざっくりまとめると下記のようになります。

|パラメータ| 内容 | jQueryで取得 |JavaScriptで取得 |
|:-:|:-:|:-:|:-:|:-:|
| A |  ウィンドウのスクロール量 (ページの上端からの距離) |$(window).scrollTop()| window.pageYOffset |
| B |  表示領域の高さ |$(window).height()|document.documentElement.clientHeight|
| C | 要素の位置 (ページの上端からの距離)| $(elm).offset().top|elm.getBoundingClientRect().top|
| D | 要素の高さ| $(elm).outerHeight()|elm.getBoundingClientRect().height|

##表示条件の考え方

取得したパラメータを組み合わせ、条件式を組み立てます。

- ある要素が表示領域に見え出したら:　*A + B > C*
- ある要素が表示領域に完全に表示されたら:　*A + B > C + D*
- ある要素が表示領域から見えなくなったら:　*A > C + D*


#jQueryを使用する場合

##各パラメータの取得

A. ウィンドウのスクロール量の取得

```index.js
let scrollPos = $(window).scrollTop();
```
ページの上端を基準とし、表示領域のスクロール位置（Y座標）をpx値で返します。

B. 表示領域の高さの取得

```index.js
let winH = $(window).height();
```
メニューバーなどを含まない、表示領域の高さをpx値で返します。

C. 要素の位置の取得

```index.js
let elmOffset = $('#box').offset().top;
```

`offset()`は、ある要素が配置されている位置を取得するメソッドです。
`offset().top`で、ページの上端を基準とし、要素の上端の位置（Y座標）をpx値で返します。

D. 要素の高さの取得

```index.js
let elmH = $('#box').outerHeight();
```
`outerHeight()`で要素の高さをpx値で返します。これは、paddingやborderを含む外側の高さです。
optionで、`outHeight(true)`とすると、さらにmargin値を含む高さになります。
似たメソッドで`innerHeight()`がありますが、これは、padding/borderを含まない内側の高さを返します。

##実装例

例として、スクロールによって、ある要素（#box3）が表示されると、追従ボタン（#sticky）を非表示になるようなサンプルを作成しました。

```index.js
$(function() {
  let scrollPos; //スクロール量
  let elmOffset; //要素(#box3)の位置
  let winH = $(window).height(); //表示領域の高さ
  $(window).scroll(function() {
    scrollPos = $(window).scrollTop();
    elmOffset = $('#box3').offset().top;
    if (scrollPos + winH > elmOffset) {
      $('#sticky').removeClass('is-active');
    } else {
      $('#sticky').addClass('is-active');
    }
  });
  $(window).resize(function() {
    winH = $(window).height();
  });
})
```

```index.html
   <div id="sticky is-active" class="sticky"></div>
   <div class="container">
    <div id="box1" class="box"><p>コンテンツ１</p></div>
    <div id="box2" class="box"><p>コンテンツ２</p></div>
    <div id="box3" class="box"><p>コンテンツ３</p></div>
   </div>
```

```style.css
    .container {
      width: 1100px; 
      margin: 50px auto;
    }
    .container .box {
      height: 800px;
    }
    .sticky {
      position: fixed;
      bottom: -100px;
      left: 0;
      width: 100%;
      background: #ccc;
      height: 50px;
      transition: all 0.6s ease-in-out;
      z-index: 1;
    }
    .sticky.is-active {
      bottom: 0;
    }
```

#JavaScriptのみで実装する場合

##各パラメータの取得

A. ウィンドウのスクロール量の取得

```index.js
let scrollPos = window.scrollY; //IE非対応
let scrollPos2 = window.pageYOffset;
let scrollPos3 = document.documentElement.scrollTop;
```
いずれも、ページの上端を基準とし、表示領域のスクロール位置（Y座標）をpx値で返します。

scrollY: https://developer.mozilla.org/ja/docs/Web/API/Window/scrollY
pageYOffset: https://developer.mozilla.org/en-US/docs/Web/API/Window/pageYOffset
scrollTop: https://developer.mozilla.org/ja/docs/Web/API/Element/scrollTop

B. 表示領域の高さの取得

```index.js
let winH = document.documentElement.clientHeight;
```

メニューバーなどを含まない、表示エリアの高さをpx値で返します。
似たプロパティに、`window.innerHeight`がありますが、こちらは、水平スクロールバーの高さを含みます。また、`window.outerHeight`の場合は、ブラウザ のメニューバーなどを含む外側の高さを返します。

clientHeight: https://developer.mozilla.org/ja/docs/Web/API/Element/clientHeight
innerHeight: https://developer.mozilla.org/ja/docs/Web/API/Window/innerHeight
outerHeight: https://developer.mozilla.org/ja/docs/Web/API/Window/outerHeight


C. 要素の位置の取得

```index.js
let elm = document.getElementById('box');
let elmOffset = elm.offsetTop;
let elmOffset2 = elm.getBoundingClientRect().top;
```

いずれもページの上端を基準とし、要素の上端の位置（Y座標）をpx値で返します。

offsetTop: https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetTop
getBoundingClientRect: https://developer.mozilla.org/ja/docs/Web/API/Element/getBoundingClientRect


D. 要素の高さの取得

```index.js
let elmH = elm.offsetHeight;
let elmH2 = elm.getBoundingClientRect().height;
let elmH3 = elm.clientHeight;  //border含まない
```
`offsetHeight`、`getBoundingClientRect().height`は、Padding, borderを含む、要素の高さをpx値で返します。`clientHeight`の場合は、Paddingは含まれますが、borderは含まれません。


offsetHeight: https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetHeight
getBoundingClientRect: https://developer.mozilla.org/ja/docs/Web/API/Element/getBoundingClientRect
clientHeight: https://developer.mozilla.org/ja/docs/Web/API/Element/clientHeight

##実装例
先ほどのjQueryでの実装例をJavaScriptで置き換えたサンプルです。

```index.js
document.addEventListener('DOMContentLoaded', function() {
  let elm = document.getElementById('box3');
  let elmSticky = document.getElementById('sticky');
  let scrollPos;
  let elmOffset;
  let winH = document.documentElement.clientHeight;

  window.addEventListener('scroll', function() {
    scrollPos = window.pageYOffset;
    elmOffset = getBoundingClientRect().top;
    if (scrollPos + winH > elmOffset) {
      elmSticky.classList.remove('is-active');
    } else {
      elmSticky.classList.add('is-active');
    }
  })
  window.addEventListener('resize', function() {
    winH = document.documentElement.clientHeight;
  })
})
```