iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 26
0
Modern Web

React + D3 的正確姿勢系列 第 26

Day26-donut chart(using D3.js)

前言

昨天的文章介紹了圓餅圖,今天要來介紹圓餅圖的變形版:環圈圖,其實筆者自己比較少用圓餅圖,只要是這種要計算數據比例的統計圖現在都改用環圈圖了,至於為什麼會有這麼大的轉變就讓我們繼續看下去吧XD

環圈圖

環圈圖就是把圓餅圖中心挖空的變形版,所以使用時機的部分還是跟圓餅圖一樣,適用於觀察統計資料各分量佔總量的比例,但就如剛剛所說環圈圖把圓餅圖的中心挖空了,這麼做有什麼好處呢?接下來筆者一一為大家揭曉這張圖形相較於圓餅圖來說有哪些優點以及缺點吧!

  • 優點

    • 節省空間

      大家在設計圓餅圖的時候通常都會把圖表標題擺在上方,這樣做不是不好但就視覺上來說好像圖表跟標題比較沒有關聯感,但環圈圖把中心挖空了,挖空的部分就可以填入圖表標題,這時候就會覺得這個標題跟圖表有很大的關聯性了,亦或者是想要設計 tooltip 可是在圓餅圖上就很容易因為滑鼠角度的問題讓這個 tooltip 顯示到其他的區塊內了,但這時候就可以利用挖空的部分把 tooltip 資訊顯示在中心上,這也是個很好的作法。

  • 缺點

    • 難辨識角度

      由於環圈圖把圓餅圖的中心挖空,假如今天想了解該筆資料大概佔了圖形多少角度,這時候就比較難判斷了,但相信大家應該都會覺得這個缺點應該還好才對,畢竟我們設計統計圖表就是為了知道哪些群組站的比較多而已,應該比較少是想知道角度的XD

繪製流程

講了那麼多廢話後終於開始進入繪製流程了,其實環圈圖跟圓餅圖只有一個地方不一樣而已,剩下的作法都一樣,但為了讓這幾篇在介紹圖表的文章排版都差不多,所以這邊筆者也是會把完整的步驟寫出來給大家參考。

首先一樣是初始化 svg 容器

const width = document.querySelector(`#${root}`).clientWidth,
    height = width
    
const svg = d3
  .select(document.querySelector(`#${root}`))
  .append('svg')
  .attr('width', width)
  .attr('height', height)
  .append('g')
  .attr('transform', `translate(${width / 2}, ${height / 2})`)

接下來就是設定弧線,這裡就是跟圓餅圖不一樣的地方了,由於環圈圖中間被挖空,所以 innerRadius 不會設為 0 ,這邊看讀者想設定什麼數字都行,只要記住不要比半徑大就行了,不然內圈比外圈大好像怪怪的XD

這邊筆者為求美觀(?)所以 innerRadius 的部分就設定成比半徑少 30 而 outerRadius 就一樣維持半徑的大小。

const arc = d3
  .arc()
  .innerRadius(width / 2 - 30)
  .outerRadius(width / 2)

設定好弧線後接下來就是要初始化環圈圖了,跟圓餅圖一樣這邊也會用到 d3.pie() ,畢竟前面有說環圈圖就是圓餅圖的變形而已XD

const pie = d3.pie().value(d => d.value)

最後就是把資料傳進去圖表內了,這邊寫法也跟圓餅圖也一模一樣。

svg
  .selectAll('path')
  .data(pie(datas))
  .enter()
  .append('g')
  .append('path')
  .attr('d', arc)
  .attr('fill', d => chartColor[d.name].color)

最後筆者要來提一下如何在環圈圖的中心加上 tooltip ,這邊就不用 d3-tip 的方式來處理,畢竟只要加個 text 進去就好,用到 d3-tip 就有點大材小用了XD

接下來提一下 tooltip 的設計流程,首先是 mouseover 的時候

  1. 先建立一個 group 用來存放整個 tooltip 內容
  2. 在這個 group 內新增兩個 text 文字負責存放 tooltip 標題以及內文
  3. 為求美觀將 text 利用 text-anchor: middle 的方式移動到中間
  4. 最後為了讓內文可以正確的擺放在標題下,這邊將標題的 dy 設為負數讓他往上移動,並將內文的 dy 設為正數讓他往下移動

mouseout 就單純很多,只要把 text 移除就好,這邊筆者為了確保不會刪到其他的 text 所以會用 className 的方式移除 text ,所以整體的寫法就像這樣:

svg
  .on('mouseover', function(d) {
    const g = d3
      .select(this)
      .append('g')
      .attr('class', 'text-group')

    // tooltip title setting
    g.append('text')
      .attr('class', 'date')
      .text(`${d.data.name}`)
      .attr('text-anchor', 'middle')
      .attr('dy', '-1.2em')

    // tooltip content setting
    g.append('text')
      .attr('class', 'value-text')
      .text(`點擊數 ${d.data.clicks}`)
      .attr('text-anchor', 'middle')
      .attr('dy', '.6em')
  })
  .on('mouseout', function(d) {
    d3.select('.text-group').remove()
  })

組合起來

最後筆者有把上面流程所講的程式碼都丟到 GitHub 上,有興趣想要參考的讀者歡迎上去參考這些範例碼。

donut chart

總結

今天介紹了環圈圖不曉得讀者以後會不會也跟筆者一樣捨棄圓餅圖呢?雖然筆者比較沒有什麼審美觀,但筆者還是覺得環圈圖比圓餅圖好看很多,所以大家還是盡量多用環圈圖吧(推坑中XD)

如果對於文章有任何問題歡迎在下面留言給我,沒問題的話明天要來介紹最後一個圖表:氣泡圖了。


上一篇
Day25-pie chart(using D3.js)
下一篇
Day27-bubble chart(using D3.js)
系列文
React + D3 的正確姿勢30

尚未有邦友留言

立即登入留言