D3.js —— 繪製柱狀圖(二)

tonychen發表於2023-02-11

前言

上一篇文章 我們講了 D3.js 中一些常見的 API,並且透過一個 demo 瞭解了 D3.js 是如何工作的。鋪墊完之後,這篇文章我們正兒八經的來實現一個柱狀圖。

正文

比例尺

比例尺是 D3.js 中一個重要的概念,它用於在值和座標之間的相互轉換。本文中出現的相關 API 如下?

API說明
scaleBand一般用於將一組離散值對映到連續範圍的值
scaleLinear線性轉換,將連續的輸入域對映到連續的輸出範圍

想象一下,我們需要在一個 800 x 600 寬高的容器內繪製一個座標系。通常情況下,座標的數值不會與實際的寬高完全對應。因此,我們需要一種工具來將實際的寬高與座標系中的數值進行對映。

對於 scaleLinear 這個線性轉換函式,可以用下面的公式來描述:

$y = mx + b(b 為任意常量)$

透過輸入 $x$(通常是一個數值),我們可以獲得元素在頁面上的高度或寬度。

繪製座標系

首先我們先新增初始程式碼:引入 D3.js & 設定基礎樣式

<html>
<head>
    <title>Bar</title>
    <style>
        #bar {
            display: flex;
            align-items: center;
            justify-content: center;
            width: 100%;
            height: 100%;
        }
    </style>
</head>
<body>
<div id="bar"></div>
</body>
<script src="<https://d3js.org/d3.v7.min.js>"></script>
</html>

緊接著我們來建立一個 svg,寬高為:800 x 600

const width = 800;
  const height = 600;
  const data = Array.from({length: 26}).map((_, i) => ({
    name: String.fromCharCode(65 + i),
    value: Math.random() * 100
  }))

  const svg = d3.select('#bar')
    .append('svg')
    .attr('width', width)
    .attr('height', height)

此時頁面上依舊是光禿禿的一片,沒關係,我們先把 x 軸加上:

const xScale = d3.scaleBand().domain(data.map(item => item.name)).range([0, 500]).padding(0.1)
const xAxis = d3.axisBottom(xScale)
svg.append('g').call(xAxis)

此時,頁面已經出現了我們需要的 x 軸

image.png
隨後我們把 y 軸也加到頁面上:

const yScale = d3.scaleLinear().domain([0, 100]).range([500, 0])
const yAxis = d3.axisLeft(yScale)
svg.append('g').call(yAxis)

此時頁面就會出現一個奇怪的座標系???

當然如果你看完了上一篇文章,你就知道這是因為座標原點在左上角導致的?

image.png
因此我們在繪製 x 軸的時候還需要把它的位置調整一下

svg.append('g').attr('transform', `translate(0, 500)`).call(xAxis

那麼寫到這裡,我們就得到了一個長得挺好看的座標系

image.png

新增柱狀圖

新增座標系之後,我們就可以開始新增柱狀圖了。

在此之前有一些細節需要說明:我們可以利用 xScaleyScale 來根據網頁上實際需要展示的值,來決定柱狀圖的展示位置。比如,下面這段程式碼:

const xScale = d3.scaleBand().domain(data.map(item => item.name)).range([0, 500]).padding(0.1)

執行之後,我們可以透過 name → value 的對映,來獲取 x 軸上每一個離散點的具體位置,同理 y 軸也是如此。

根據這點,我們就不需要再花太多時間計算每個矩形的的 x、y 座標了,而是能直接透過 xScaleyScale 這些函式來進行簡單的換算來達到我們想要的效果。程式碼如下:

svg.selectAll('rect')
    .data(data)
    .join('rect')
    .attr('x', item => xScale(item.name))
    .attr('y', item => yScale(item.value))
    .attr('width', xScale.bandwidth())
    .attr('height', item => 500 - yScale(item.value))
    .attr('fill', 'steelblue')

效果如下?

image.png

補充動畫

D3.js 中的動畫邏輯是這樣的:

  1. 設定初始值,比如 x、y
  2. 新增動畫配置,比如動畫函式、持續時間等等
  3. 設定最終值,比如 widthheight
svg.selectAll('rect')
    .data(data)
    .join('rect')
    // 設定 x 座標,防止動畫時出現抖動
    .attr('x', item => xScale(item.name))
    .attr('y', 500)
    // 設定寬度,柱狀圖的寬度在這裡一直保持不變即可
    .attr('width', xScale.bandwidth())
    // 為了有動畫效果,這裡先設定高度為 0
    .attr('height', 0)
    .transition()
    // 設定延遲
    .delay((item, i) => i * 20)
    // 新增動畫函式
    .ease(d3.easeCubic)
    // 設定動畫時長
    .duration(1000)
    // 最終的效果
    .attr('y', item => yScale(item.value))
    .attr('height', item => 500 - yScale(item.value))
    .attr('fill', 'steelblue')

2023-02-09 231642.gif

總結

本文中我們瞭解了比例尺並且熟悉了 D3.js 中一些與之相關的 API ,並且透過一系列步驟瞭解了怎麼透過 D3.js 來實現一個柱狀圖。

想要了解更多前端知識,歡迎關注我的公眾號:tony老師的前端補習班

系列文章:

相關文章