CSS Grid Layoutでガントチャートを作る


Webアプリ制作には、管理者ページが付き物。商品管理やユーザー管理、アプリ全体の設定など管理者が行うためのWebページ設置は必然的といえる。

その管理Webページで求められる機能のひとつに、ガントチャートがある。スケジュールやシフトを確認するための棒グラフの一種だ。

このエントリーでは、ガントチャートCSS Grid Layoutでコーディングする方法を紹介したい。

ガントチャートを作る

以下のガントチャートは、シフト表をイメージしている。8時〜23時までの15分単位で、勤務時間を棒線で示す。棒線の色の違いは、シフトパターンやスタッフの雇用形態などを想定してのことだ。

全体に関わるスタイルの差異によってはイメージしたものにならないかもしれない。その場合は、都合よく調整してほしい。

HTML

<div class="chart">
  <div class="chart-mask">
    <div class="chart-inner">
      <!-- // [Sample] 15フレーム // -->
      <div class="chart-layer" style="grid-template-columns: 80px repeat(15, 28px); -ms-grid-columns: 80px (28px)[15];">
        <div class="chart-cell" style="grid-column: 1; -ms-grid-column: 1;"></div>
        <div class="chart-cell" style="grid-column: 2; -ms-grid-column: 2;" data-hour="8"></div>
        <div class="chart-cell" style="grid-column: 3; -ms-grid-column: 3;" data-hour="9"></div>
        <div class="chart-cell" style="grid-column: 4; -ms-grid-column: 4;" data-hour="10"></div>
        <div class="chart-cell" style="grid-column: 5; -ms-grid-column: 5;" data-hour="11"></div>
        <div class="chart-cell" style="grid-column: 6; -ms-grid-column: 6;" data-hour="12"></div>
        <div class="chart-cell" style="grid-column: 7; -ms-grid-column: 7;" data-hour="13"></div>
        <div class="chart-cell" style="grid-column: 8; -ms-grid-column: 8;" data-hour="14"></div>
        <div class="chart-cell" style="grid-column: 9; -ms-grid-column: 9;" data-hour="15"></div>
        <div class="chart-cell" style="grid-column: 10; -ms-grid-column: 10;" data-hour="16"></div>
        <div class="chart-cell" style="grid-column: 11; -ms-grid-column: 11;" data-hour="17"></div>
        <div class="chart-cell" style="grid-column: 12; -ms-grid-column: 12;" data-hour="18"></div>
        <div class="chart-cell" style="grid-column: 13; -ms-grid-column: 13;" data-hour="19"></div>
        <div class="chart-cell" style="grid-column: 14; -ms-grid-column: 14;" data-hour="20"></div>
        <div class="chart-cell" style="grid-column: 15; -ms-grid-column: 15;" data-hour="21"></div>
        <div class="chart-cell" style="grid-column: 16; -ms-grid-column: 16;" data-hour="22"></div>
      </div>
      <div class="chart-row">
        <div class="chart-head">ダミー太郎</div>
        <div class="chart-data">
          <!-- // [Sample] 60分割 = 4分割 x 15フレーム // -->
          <ul class="chart-bar fast" style="grid-template-columns: repeat(60, 7px); -ms-grid-columns: (7px)[60];">
            <li style="grid-column: 3; -ms-grid-column: 3;"></li>
            <li style="grid-column: 4; -ms-grid-column: 4;"></li>
            <li style="grid-column: 5; -ms-grid-column: 5;"></li>
            <li style="grid-column: 6; -ms-grid-column: 6;"></li>
            <li style="grid-column: 7; -ms-grid-column: 7;"></li>
            <li style="grid-column: 8; -ms-grid-column: 8;"></li>
            <li style="grid-column: 9; -ms-grid-column: 9;"></li>
            <li style="grid-column: 10; -ms-grid-column: 10;"></li>
            <li style="grid-column: 11; -ms-grid-column: 11;"></li>
            <li style="grid-column: 12; -ms-grid-column: 12;"></li>
            <li style="grid-column: 13; -ms-grid-column: 13;"></li>
            <li style="grid-column: 14; -ms-grid-column: 14;"></li>
            <li style="grid-column: 15; -ms-grid-column: 15;"></li>
            <li style="grid-column: 16; -ms-grid-column: 16;"></li>
            <li style="grid-column: 17; -ms-grid-column: 17;"></li>
            <li style="grid-column: 18; -ms-grid-column: 18;"></li>
            <li style="grid-column: 19; -ms-grid-column: 19;"></li>
            <li style="grid-column: 20; -ms-grid-column: 20;"></li>
            <li style="grid-column: 21; -ms-grid-column: 21;"></li>
            <li style="grid-column: 22; -ms-grid-column: 22;"></li>
            <li style="grid-column: 23; -ms-grid-column: 23;"></li>
            <li style="grid-column: 24; -ms-grid-column: 24;"></li>
            <li style="grid-column: 25; -ms-grid-column: 25;"></li>
          </ul>
        </div>
      </div>
      <div class="chart-row">
        <div class="chart-head">ダミー太郎</div>
        <div class="chart-data">
          <!-- // [Sample] 60分割 = 4分割 x 15フレーム // -->
          <ul class="chart-bar normal" style="grid-template-columns: repeat(60, 7px); -ms-grid-columns: (7px)[60];">
            <li style="grid-column: 18; -ms-grid-column: 18;"></li>
            <li style="grid-column: 19; -ms-grid-column: 19;"></li>
            <li style="grid-column: 20; -ms-grid-column: 20;"></li>
            <li style="grid-column: 21; -ms-grid-column: 21;"></li>
            <li style="grid-column: 22; -ms-grid-column: 22;"></li>
            <li style="grid-column: 23; -ms-grid-column: 23;"></li>
            <li style="grid-column: 24; -ms-grid-column: 24;"></li>
            <li style="grid-column: 25; -ms-grid-column: 25;"></li>
            <li style="grid-column: 26; -ms-grid-column: 26;"></li>
            <li style="grid-column: 27; -ms-grid-column: 27;"></li>
            <li style="grid-column: 28; -ms-grid-column: 28;"></li>
            <li style="grid-column: 29; -ms-grid-column: 29;"></li>
            <li style="grid-column: 30; -ms-grid-column: 30;"></li>
            <li style="grid-column: 31; -ms-grid-column: 31;"></li>
            <li style="grid-column: 32; -ms-grid-column: 32;"></li>
            <li style="grid-column: 33; -ms-grid-column: 33;"></li>
            <li style="grid-column: 34; -ms-grid-column: 34;"></li>
            <li style="grid-column: 35; -ms-grid-column: 35;"></li>
            <li style="grid-column: 36; -ms-grid-column: 36;"></li>
            <li style="grid-column: 37; -ms-grid-column: 37;"></li>
            <li style="grid-column: 38; -ms-grid-column: 38;"></li>
            <li style="grid-column: 39; -ms-grid-column: 39;"></li>
            <li style="grid-column: 40; -ms-grid-column: 40;"></li>
          </ul>
        </div>
      </div>
      <div class="chart-row">
        <div class="chart-head">ダミー太郎</div>
        <div class="chart-data">
          <!-- // [Sample] 60分割 = 4分割 x 15フレーム // -->
          <ul class="chart-bar free" style="grid-template-columns: repeat(60, 7px); -ms-grid-columns: (7px)[60];">
            <li style="grid-column: 37; -ms-grid-column: 37;"></li>
            <li style="grid-column: 38; -ms-grid-column: 38;"></li>
            <li style="grid-column: 39; -ms-grid-column: 39;"></li>
            <li style="grid-column: 40; -ms-grid-column: 40;"></li>
            <li style="grid-column: 41; -ms-grid-column: 41;"></li>
            <li style="grid-column: 42; -ms-grid-column: 42;"></li>
            <li style="grid-column: 43; -ms-grid-column: 43;"></li>
            <li style="grid-column: 44; -ms-grid-column: 44;"></li>
            <li style="grid-column: 45; -ms-grid-column: 45;"></li>
            <li style="grid-column: 46; -ms-grid-column: 46;"></li>
            <li style="grid-column: 47; -ms-grid-column: 47;"></li>
            <li style="grid-column: 48; -ms-grid-column: 48;"></li>
            <li style="grid-column: 49; -ms-grid-column: 49;"></li>
            <li style="grid-column: 50; -ms-grid-column: 50;"></li>
            <li style="grid-column: 51; -ms-grid-column: 51;"></li>
            <li style="grid-column: 52; -ms-grid-column: 52;"></li>
            <li style="grid-column: 53; -ms-grid-column: 53;"></li>
            <li style="grid-column: 54; -ms-grid-column: 54;"></li>
            <li style="grid-column: 55; -ms-grid-column: 55;"></li>
            <li style="grid-column: 56; -ms-grid-column: 56;"></li>
          </ul>
        </div>
      </div>
      <div class="chart-row">
        <div class="chart-head">ダミー太郎</div>
        <div class="chart-data">
          <!-- // [Sample] 60分割 = 4分割 x 15フレーム // -->
          <ul class="chart-bar late" style="grid-template-columns: repeat(60, 7px); -ms-grid-columns: (7px)[60];">
            <li style="grid-column: 36; -ms-grid-column: 36;"></li>
            <li style="grid-column: 37; -ms-grid-column: 37;"></li>
            <li style="grid-column: 38; -ms-grid-column: 38;"></li>
            <li style="grid-column: 39; -ms-grid-column: 39;"></li>
            <li style="grid-column: 40; -ms-grid-column: 40;"></li>
            <li style="grid-column: 41; -ms-grid-column: 41;"></li>
            <li style="grid-column: 42; -ms-grid-column: 42;"></li>
            <li style="grid-column: 43; -ms-grid-column: 43;"></li>
            <li style="grid-column: 44; -ms-grid-column: 44;"></li>
            <li style="grid-column: 45; -ms-grid-column: 45;"></li>
            <li style="grid-column: 46; -ms-grid-column: 46;"></li>
            <li style="grid-column: 47; -ms-grid-column: 47;"></li>
            <li style="grid-column: 48; -ms-grid-column: 48;"></li>
            <li style="grid-column: 49; -ms-grid-column: 49;"></li>
            <li style="grid-column: 50; -ms-grid-column: 50;"></li>
            <li style="grid-column: 51; -ms-grid-column: 51;"></li>
            <li style="grid-column: 52; -ms-grid-column: 52;"></li>
            <li style="grid-column: 53; -ms-grid-column: 53;"></li>
            <li style="grid-column: 54; -ms-grid-column: 54;"></li>
            <li style="grid-column: 55; -ms-grid-column: 55;"></li>
            <li style="grid-column: 56; -ms-grid-column: 56;"></li>
            <li style="grid-column: 57; -ms-grid-column: 57;"></li>
            <li style="grid-column: 58; -ms-grid-column: 58;"></li>
            <li style="grid-column: 59; -ms-grid-column: 59;"></li>
            <li style="grid-column: 60; -ms-grid-column: 60;"></li>
          </ul>
        </div>
      </div>
    </div>
  </div>
</div>

SCSS

.u-chart {
  + .u-chart {
    margin-top: 30px;
  }
  .chart-mask {
    overflow-x: auto;
    overflow-y: hidden;
  }
  .chart-inner {
    position: relative;
    margin-top: 23px;
    padding-top: 6px;
    padding-bottom: 6px;
    border-top: 1px solid #E5E5E5;
    border-bottom: 1px solid #E5E5E5;
  }
  .chart-layer {
    display: grid;
    grid-template-rows: auto;
         -ms-grid-rows: 9999px;
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    margin: auto;
  }
  .chart-cell {
    position: relative;
    grid-row: 1;
    &:not(:first-child) {
      border-left: 1px solid #E5E5E5;
    }
    &:not(:first-child):before {
      content: attr(data-hour);
      position: absolute;
      top: -22px;
      left: -.5em;
      font-size: 12px;
    }
  }
  .chart-row {
    display: grid;
    grid-template-columns: 80px 1fr;
    grid-template-rows: auto;
    position: relative;
    z-index: 1;
  }
  .chart-head {
    grid-column: 1;
    grid-row: 1;
    padding-top: 2px;
    padding-bottom: 2px;
    font-size: 12px;
  }
  .chart-data {
    grid-column: 2;
    grid-row: 1;
    position: relative;
  }
  .chart-bar {
    display: grid;
    grid-template-rows: 12px;
    position: absolute;
    top: 50%;
    left: 0;
    transform: translateY(-50%);
    li {
      &:first-child {
        border-radius: 3px 0 0 3px;
      }
      &:last-child {
        border-radius: 0 3px 3px 0;
      }
    }
    &.fast li {
      background-color: #FDC44F;
    }
    &.normal li {
      background-color: #33BFDB;
    }
    &.late li {
      background-color: #F58E7E;
    }
    &.free li {
      background-color: #79D1B0;
    }
  }
}

まとめ

以上が、ガントチャートを構成するHTML,CSSだ。

ご覧のとおりHTMLには、インラインスタイルばかりで気になるかもしれないが、動的にフレーム数が増減したり、grid-columnの開始,終了地点が変動したりを考慮して、やむを得ずだ。ちゃんと分離する方法があるなら、そうするとよい。

インラインスタイルのプロパティで-msから始まるものは、IE用のベンダープレフィックスだ。IE10,11に適用されるはずだが、確認はしていない。IE対応が必要なら必ずチェックするように!

このエントリーが、あなたのクリエイティビティを刺激するものであると期待したい。


Leave a Reply

Your email address will not be published. Required fields are marked *