railsアプリでの追従バナーの実装サンプル

# 概要
Webページの左下に追従表示される画像バナーの参考レシピを記載しています。
※一番肝心な「jsのaddScrollListener関数」の解説がまだできていないので、そのあたりよしなに準備できる方の参考程度に。。。そのうち追記します。

## 画像ファイル
任意の画像を準備し`anavi/app/assets/images`配下に格納します。以降のサンプルコードでは「xxxx.png」とします。

## ビューファイル
###Ruby on Rails(slim表記)
以下のようなパーシャルファイルを作成し、追従バナーの表示が必要なページでrenderして呼び出します。
※[slim表記](https://github.com/slim-template/slim/blob/master/README.jp.md)で記載しています。

```ruby:app/views/shared/_fixed_banner.html.slim
.fixed_banner
  = link_to "リンクさせたいページURL", target: :_blank, rel: :noopener
    = image_tag "xxxx.png", alt: "代替文言を記載"

- content_for :local_js do
  script type="text/javascript" src="/assets/javascripts/xxxx.min.js"
  javascript:
    setStartScroll();
```

```ruby:app/views/xxxx/xxxx.html.slim(追従バナーを設置したいビューファイル)
- # 追従バナーを描画する
= render 'shared/fixed_banner'
```

`- content_for :local_js do`の部分は個別のviewファイル内にjavascriptを直接書き込む場所として利用していて、ここでライブラリ読み込んだり、処理を直接書いたりしておいたものを、全ページ共通のテンプレートに呼び出した時に、以下のように生成させています。

```ruby:全ページ共通レイアウト(application.html.erbに当たるようなファイル)
 = yield :local_js
```

### HTMLに変換された後

```html:
<div class="fixed_banner">
  <a target="_blank" rel="noopener" href="リンクさせたいページURL">
    <img alt="代替文言を記載" src="/assets/xxxx.png">
  </a>
</div>

・・・

<!-- content_for :local_js doで書いていたものも読み込まれている-->
<script>省略</script>
<script>省略</script>
```

## スタイルシート
scss記法のsassで書いています。画面が表示された段階では非表示で、下にスクロールすると画面左下に追従表示され、ページ最下部まで画面スクロールすると非表示となるようにしています。 &.show　と　&.endが　それぞれの状態でのスタイル。またスマートフォン表示では非表示となるようにしています。
### scss
```scss:app/assets/stylesheets/xxxx.css.scss
/* 画面追従のバナー */
.fixed_banner {
  bottom: 14px;
  display: flex;
  left: 14px;
  opacity: 0;
  position: fixed;
  transition: opacity 400ms ease-in-out, transform 600ms cubic-bezier(0.165, 0.84, 0.44, 1);
  width: 350px;
  z-index: 99;
  &.show {
    opacity: 1;
  }
  &.end {
    opacity: 0;
  }
  img{
    border-radius: 4px;
    width: 100%;
  }
}
/*sp style*/
@media screen and (max-width: 767px) {
  .fixed_banner{
    display: none;
  }
}
```

### cssに変換された後
```css:
/* 画面追従のバナー */
.fixed_banner {
  bottom: 14px;
  display: flex;
  left: 14px;
  opacity: 0;
  position: fixed;
  transition: opacity 400ms ease-in-out, transform 600ms cubic-bezier(0.165, 0.84, 0.44, 1);
  width: 350px;
  z-index: 99;
}

.fixed_banner.show {
  opacity: 1;
}

.fixed_banner.end {
  opacity: 0;
}

.fixed_banner img {
  border-radius: 4px;
  width: 100%;
}

/*sp style*/
@media screen and (max-width: 767px) {
  .fixed_banner {
    display: none;
  }
}
```

## DOM操作
共通レイアウトとして、「ページ最上部のヘッダー」や「ページ最下部のコピーライト表記」があるためそれらの位置を基準にページのスクロール位置を判定して、それに応じて`<div class="fixed_banner">・・・</div>`要素に"show"や"end"のclassを追加したり、削除したりしています。それに応じて適応されるcssが変わり、表示や非表示が制御されます。

```javascript:app/assets/javascripts/xxxx.js
//画面スクロールをトリガーに、追従バナーの表示と非表示を制御する
function setStartScroll(){
  var _target = $('header.fixed-top');
  if (_target[0]){
    var _targetT = _target.offset().top;
    var $fixed_banner = $('.fixed_banner');

    Shared.util.addScrollListener(function(t,b) {
      if (t > 40) {
        $fixed_banner.addClass('show');
      }else {
        $fixed_banner.removeClass('show');
      }
      if (b > $('.copyright_wrap').offset().top) {
        $fixed_banner.addClass('end');
      }else {
        $fixed_banner.removeClass('end');
      }
    });
  }
};
```

addScrollListenerは別のライブラリで定義している関数です。パーシャルファイルのlocal_jsなかでxxxx.min.jsで読み込んでいます。位置判定しています。

# 参考文献
[slimについて](https://github.com/slim-template/slim/blob/master/README.jp.md)
[content_forやyieldについて](https://railsguides.jp/layouts_and_rendering.html#content-for%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B)