Bootstrap4のCardsとMasonry.jsの相性が最強だった件

# 概要

[テニスプレイヤーがLaravelでWebサービス作ってみた](https://qiita.com/UNKCHAN/items/59c8be24321524b0dbf8)の個別記事です。

[Masonry.js](https://masonry.desandro.com/)とは画像などの矩形オブジェクトをきれいに整列させてくれるjQueryプラグインです。
[Cards](https://getbootstrap.com/docs/4.0/components/card/)はBootstrap4から導入された新しいコンポーネントです。矩形の範囲に文字列や画像などを挿入して並べることができます。以前のバージョンではpanelという名前でしたが、グリッドレイアウトでの使い勝手が良くなってCardsという名前で再登場しました。
この二つを組み合わせると、**伝えたい情報をパネル状にして、簡単に整列**させることができます。しかも**どんな大きさの画面でも見やすいレスポンシブデザイン**になります。

表形式で羅列するのもいいんですが、味気ないので情報の伝え方を少し工夫したいと思っていました。

# 動作サンプル

https://teniguru.tk/

この技術を使ってアプリを作ってみました。画面の大きさを変えるとCardsがうねうね動いて、見ているだけで(自分は)楽しいです。

# コード

### HTML
必要なパッケージを読み込みます。

```html
<!-- jQuery -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.0/umd/popper.min.js" integrity="sha384-cs/chFZiN24E4KMATLdqdvsezGxaGsi4hLGOzlXwp5UZB1LY//20VyM2taTB4QvJ" crossorigin="anonymous"></script>
<!-- Bootstrap4 -->
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js" integrity="sha384-uefMccjFJAIv6A+rW+L4AHf99KvxDjWSu1z9VI8SKNVmz4sk7buKt/6v9KI65qnm" crossorigin="anonymous"></script>
<!-- Masonry -->
<script src="https://unpkg.com/masonry-layout@4.2.1/dist/masonry.pkgd.min.js"></script>
```

続いてbodyの内容を書きます。

```html
<div class="container">
  <!-- .grid-indexでCards全体をネストする -->
  <div class="grid-index">
    <!-- ウインドウサイズごとにグリッドサイズを定義。スマホだと横に1つ、タブレットだと2つ、PCだと3つ並べる -->
    <div class="grid-sizer col-12 col-sm-6 col-lg-4"></div>

    <!-- Card1 -->
    <div class="grid-item col-12 col-sm-6 col-lg-4">
      <div class="card">
        <div class="card-block">
          <div class="card-title">
            <h5>
              Card1のタイトル
            </h5>
          </div>
          <div class="card-text">
            Card1の内容
          </div>
        </div>
      </div>
    </div>
    <!-- Card1ここまで -->

    <!-- Card2 -->
    <div class="grid-item col-12 col-sm-6 col-lg-4">
      <div class="card">
        <div class="card-block">
          <div class="card-title">
            <h5>
              Card2のタイトル
            </h5>
          </div>
          <div class="card-text">
            Card2の内容
          </div>
        </div>
      </div>
    </div>
    <!-- Card2ここまで -->

    <!-- Card3 -->
    <div class="grid-item col-12 col-sm-6 col-lg-4">
      <div class="card">
        <div class="card-block">
          <div class="card-title">
            <h5>
              Card3のタイトル
            </h5>
          </div>
          <div class="card-text">
            Card3の内容
          </div>
        </div>
      </div>
    </div>
    <!-- Card3ここまで -->

  </div> <!-- .grid-index -->
</div> <!-- .container -->
```

ここまででもグリッドレイアウトになり、レスポンシブもしてくれます。
しかし同じ行のCardsの高さがまちまちだと、次の行のCardが詰まってくれないので隙間が空いてしまい、見た目が良くないです。
![card (1).png](https://qiita-image-store.s3.amazonaws.com/0/240305/2e6acd42-cf0b-917a-a9fa-860e44fd0e3a.png)

Masonryを使うことで解決します。

### jQuery

```js
$(function(){
    $('.grid-index').masonry({
        itemSelector: '.grid-item',
        columnWidth: '.grid-sizer',
        percentPosition: true
    });
});
```

基本的にこれだけです。隙間が詰まって整然と並んでくれます。ウインドウサイズを変えるとうねうねするようになります。

時々Cardsが全部重なったまま並んでくれないことがあります。
その場合はコンポーネントをすべて読み込んでからMasonryを発火させるために動作遅延の処理を入れてあげます。

```js
$(function(){
   //読み込み完了後0.2秒待ってからmasonryする
    var cnt = 0;
    var waiting = setInterval(function(){
        if(cnt == 1) {

            $('.grid-index').masonry({
                itemSelector: '.grid-item',
                columnWidth: '.grid-sizer',
                percentPosition: true
            });

            clearInterval(waiting);
        }
        cnt++;
        }, 200);
});
```