用 CSS Grid 佈局製作一個響應式柱狀圖

前端小蜜蜂發表於2020-05-26

最新一段時間比較喜歡玩弄圖表,出於好奇,我想找出比較好的用 CSS 製作圖表的方案。開始學習網上開源圖表庫,它對我學習新的和不熟悉的前端技術很有幫助,比如這個:CSS Grid。

今天和大家分享我學到的新知識:如何用 CSS Grid 佈局製作一個普通的響應式柱狀圖。先上效果圖:

這篇文章的示例只是一個試驗,用來學習 CSS Grid 佈局,加上本人也是現學現賣,所以本文出現的程式碼不具有太多的參照意義。

第一個簡單版本

第一眼看上去可能會有點不知道怎麼開始,因此我們先來關注如何建立一個簡單的版本。首先,我們需要為圖表編寫 HTML 標籤:

<div class="chart">
  <div class="bar-1"></div>
  <div class="bar-2"></div>
  <div class="bar-3"></div>
  <div class="bar-4"></div>
  <!-- 一直到 bar-12 -->
</div>

這些 bar- 開頭的 div 標籤將對應柱狀圖中的一條柱子,整篇文章所需要的 HTML 就這麼多。

現在按照我的步驟和簡單的解說一步一步用 CSS 把柱狀圖大概的樣式畫出來,不用過多地擔心下面出現的可能對你有些陌生的 CSS 語義,稍後我們將重點介紹關於 CSS Grid 的知識。

好了,現在開始我們的 CSS 樣式編寫。我們先對父元素新增一些必要的樣式:

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  background-color: #eee;
  display: flex;
  justify-content: center;
}

.chart {
  height: 100vh;
  width: 70vw;
}

我們需要在圖表中有 12 個條形,中間有 5px 的間距,按此需求,我們可以對父類 .chart 編寫如下 Grid 相關的樣式:

.chart {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  grid-template-rows: repeat(100, 1fr);
  grid-column-gap: 5px;
}

對於熟悉 Grid 佈局的人來說,這是非常簡單的。上面程式碼表達的是:“我想要 12 列,每個子元素具有相同的寬度(1fr = 1 fraction),高度分為 100 等分,1 等分為一行(這樣方便計算),它們之間有 5px 的間隔。”

到這裡,我們的圖表仍然是空的,因為我們沒有告訴我們的子元素如何去佔用網格中的空間。我們使用 grid-row-startgrid-row-end 屬性來填充網格中的垂直空間,後而我們將通過改變這兩個屬性來定義各個子元素自己的高度。為樣式類為 bar 開頭子元素新增如下樣:

[class*='bar'] {
  grid-row-start: 1;
  grid-row-end: 101;
  border-radius: 5px 5px 0 0;
  background-color: #ff4136;
}

現在可以得到這樣的效果:

我們告訴每個柱狀圖從網格的頂部(1)開始,然後在底部(101)結束。上面我們把網格劃分了 100 行,為什麼要使用 101 作為該屬性的值呢?如果你被這些 Grid 屬性搞蒙了,沒關係!在我們繼續之前,讓我們對此進行一點探討。

理解網格線

Grid 佈局的一個特殊之處就是網格線的概念,這對理解這個新的佈局工具非常重要。以下是網格線在四行四列網格中繪製的示意圖:

這四行四列的對應的樣式是這樣的(特殊的黑色區域對應的樣式類為 special-col):

.grid {
  grid-gap: 5px;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(4, 1fr);
}

.special-col {
  grid-row: 2 / 4;
  background-color: #333;
}

grid-rowgrid-row-startgrid-row-end 的簡寫屬性,前者表示元素在網格中的開始位置,後者表示元素在網格中的結束位置。注意到沒,黑色塊是從第 2 條網格線開始的,並在第 4 條網格線結束(而不是在第 4 行)。如果我們想讓那個黑盒子填滿所有 4 行,那麼我們需要在第 5 條網格線結束,即:1 / 5。理解這一點很重要。

換句話說,我們不應該認為子元素在一個網格中佔據整個行或列,而應該只跨越這些網格線的。我花了不少時間才從概念上理解並習慣了這一點,因為我最近深入研究了 Jen Simmons 關於這個問題的教程。

回到示例

這就是為什麼在我們上面的圖表示例中,所有列都在 101 這個值結束,因為 101 代表的是第 101 條網路線,而不是第 100 行。

現在,由於我們的 .chart 使用了 vw/vh 單位,也就有了一個響應良好的圖表,不需要再做其它的額外工作來支援響應式。如果你調整瀏覽器大小,你會發現它可以很好地壓縮或延伸,它總是佔據整個視窗。

理解了網路線的概念,我們就可以很輕鬆地為柱子調整高度了,我們需要讓各柱子高度參差不一。

.bar-1 {
  grid-row-start: 55;
}
.bar-2 {
  grid-row-start: 1;
}
...(略);

最後我們使寄偶數的柱子顏色不一樣:

[class*='bar']:nth-child(odd) {
  background-color: #ff4136;
}

[class*='bar']:nth-child(even) {
  background-color: #0074d9;
}

效果:

我們就這樣製作完成了一個支援響應式的柱狀圖。當然,這個示例只是一個開始,距離要達到實際應用的效果還有很多事情要做。比如畫標註和軸、通過 JS 來繫結真實的業務資料等。

相關文章