iT邦幫忙

2

D3.js - 圖表繪製

Ares 2019-10-22 11:37:103849 瀏覽
  • 分享至 

  • xImage
  •  

上一篇介紹了 D3.js 的基礎,這篇會邊繪製圖表邊介紹繪製的方法,我們會使用到 SVG,還不熟悉 SVG 也可以參考下面的文章哦
SVG - 基礎圖形與樣式
SVG - 進階功能介紹

方法

因為 D3 實在有太多方法了,這邊我們只介紹比例尺座標軸的繪製,如果想了解所有方法可以參考這裡

比例尺

繪製圖表時,資料常常有大有小,而比例尺就是將資料等比例轉換到某個幅度內

  • d3.min():取陣列最小值
  • d3.max():取陣列最大值
  • d3.scaleLinear():建立一個比例尺
  • domain():定義資料範圍
  • range():定義圖表資料範圍
  • rangeRound():同 range,但會將結果四捨五入
  • clamp(true):將超過範圍的值設為最大值 or 最小值,預設為 false
  • unknown:將 undefinedNaN 轉為指定數值
  • ticks:將圖表資料分為數個區間
const dataset = [0, 33, 65, 199.9, 454, 887.5, 1000]

const min = d3.min(dataset) // 0
const max = d3.max(dataset) // 1000

const scale = d3
  .scaleLinear()
  .domain([min, max])
  .range([0, 100])

console.log(scale(99))   // 9.9,若使用 rangeRound 則為 10
console.log(scale(500))  // 50
console.log(scale(1001)) // 0
console.log(scale(-1))   // 0
console.log(scale(NaN))  // 母湯亂輸入

座標軸

座標軸可以讓資料與圖表更容易檢視,另外也需要用到上面說到的比例尺~

  • d3.axisTop:建立上方座標軸
  • d3.axisRight:建立右方座標軸
  • d3.axisBottom:建立下方座標軸
  • d3.axisLeft:建立左方座標軸
  • ticks:指定資料的區間數量,預設為 10
  • tickValues:顯示特定座標值,需傳入陣列
  • tickSize:調整刻線長度
  • tickPadding:調整刻線與數值的距離
  • tickFormat:可傳入函式自訂數值格式
const dataset = [0, 33, 69, 59, 100]

const chart = d3
  .select('.chart')
  .attr('width', 400)
  .attr('height', 400);
        
const scale = d3
  .scaleLinear()
  .domain([0, d3.max(dataset)])
  .range([0, 100])

const axis = d3
  .axisBottom(scale)
  .ticks(5)
  .tickSize(20)
  .tickPadding(20)
  .tickFormat(item => item + '分');

chart.append('g').call(axis)
// axis(chart.append('g'))
// 上面的方法與 call 效果相同,皆是將坐標軸繪製出來

坐標軸範例

圖表種類與繪製

上面介紹了許多基本技巧,接下來就要使用上面學到的來畫各式各樣的圖囉,html 如下

<body>
  <svg class="chart"></svg>
</body>

長條圖

// 以下七胖團員的朋友人數,當然是我掰的XD
 const sevenFat = [
  { name: 'HowHow', friend: 3 },
  { name: '蔡 Brother', friend: 13 },
  { name: '阿嘎', friend: 25 },
  { name: '馬叔叔', friend: 8 },
  { name: 'RJ', friend: 10 }
]

// 設定 svg 的寬高
const chart = d3
  .select('.chart')
  .attr('width', 500)
  .attr('height', 500);

// 選取所有的 g 群組
const group = chart
  .selectAll('g')
  .data(sevenFat)
  .enter()
  .append('g');

// 取比例尺
const scale = d3
  .scaleLinear()
  .domain([0, d3.max(sevenFat.map(item => item.friend))])
  .range([0, 300]);

// 繪製長條圖
group
  .append('rect')
  .attr('x', 100)
  .attr('y', (item, i) => i * 25)
  .attr('width', item => scale(item.friend))
  .attr('height', 25 - 2)
  .attr('fill', 'steelblue');

// 顯示名字
group
  .append('text')
  .attr('x', 0)
  .attr('y', (item, i) => i * 25 + 18)
  .style('fill', 'black')
  .style('font-size', '18px')
  .style('font-weight', 'bold')
  .text(item => item.name);

// 顯示朋友數量
group
  .append('text')
  .attr('x', (item, i) => 95 + scale(item.friend))
  .attr('y', (item, i) => i * 25 + 18)
  .attr('text-anchor', 'end')
  .style('fill', 'black')
  .style('font-size', '18px')
  .style('font-weight', 'bold')
  .text(item => item.friend);
  
// 顯示坐標軸
const axis = d3
  .axisBottom(scale)
  .ticks(5)
  .tickFormat(item => item + '人');
  
chart
  .append('g')
  .call(axis)
  .attr('transform', 'translate(100,125)');

長條圖範例

散佈圖

// 以下小弟打靶成績,中心點為 (50, 50)
 const shot = [
  { x: 10, y: 96 },
  { x: 90, y: 13 },
  { x: 77, y: 89 },
  { x: 25, y: 35 },
  { x: 13, y: 6 }
];

// 設定 svg 的寬高
const chart = d3
  .select('.chart')
  .attr('width', 500)
  .attr('height', 500);

// 選取所有的 g 群組
const group = chart
  .selectAll('g')
  .data(shot)
  .enter()
  .append('g');

// 取 X 軸比例尺
const scaleX = d3
  .scaleLinear()
  .domain([0, 100])
  .range([0, 300]);

// 顯示 X 坐標軸
const axisX = d3.axisBottom(scaleX);
chart
  .append('g')
  .call(axisX)
  .attr('transform', 'translate(30,330)');

// 取 Y 軸比例尺
const scaleY = d3
  .scaleLinear()
  .domain([100, 0])
  .range([0, 300]);

// 顯示 Y 坐標軸
const axisY = d3.axisLeft(scaleY);
chart
  .append('g')
  .call(axisY)
  .attr('transform', 'translate(30,30)');

// 繪製座標點
group
  .append('circle')
  .attr('cx', item => scaleX(item.x))
  .attr('cy', item => scaleY(item.y))
  .attr('r', '2px')
  .attr('height', 25 - 2)
  .attr('fill', 'red')
  .attr('transform', 'translate(30,30)');

// 顯示座標
group
  .append('text')
  .attr('x', item => scaleX(item.x))
  .attr('y', item => scaleY(item.y))
  .style('fill', 'black')
  .style('font-size', '10px')
  .style('font-weight', 'bold')
  .text(item => `(${item.x},${item.y})`)
  .attr('transform', 'translate(35,25)');

散佈圖範例

折線圖

折線圖就要多介紹到一些繪製 path 的方法啦~

  • d3.line:定義一個線段
  • x:傳入一個涵式,定義 x 座標
  • y:傳入一個涵式,定義 y 座標
  • curve:將路徑更改為曲線,詳細可參考這裡
// 前公司每個月產品的產量
const report = [
  { month: 1, quantity: 8561 },
  { month: 2, quantity: 9347 },
  { month: 3, quantity: 10335 },
  { month: 4, quantity: 9900 },
  { month: 5, quantity: 12012 },
  { month: 6, quantity: 10300 },
  { month: 7, quantity: 13020 },
  { month: 8, quantity: 15300 },
  { month: 9, quantity: 17210 },
  { month: 10, quantity: 13400 },
  { month: 11, quantity: 11059 },
  { month: 12, quantity: 9900 }
];

// 設定 svg 的寬高
const chart = d3
  .select('.chart')
  .attr('width', 500)
  .attr('height', 500);

// 選取所有的 g 群組
const group = chart
  .selectAll('g')
  .data(report)
  .enter()
  .append('g');

// 取 X 軸比例尺
const scaleX = d3
  .scaleLinear()
  .domain([0, d3.max(report.map(item => item.month))])
  .range([0, 300]);

// 顯示 X 坐標軸
const axisX = d3.axisBottom(scaleX);
chart
  .append('g')
  .call(axisX)
  .attr('transform', 'translate(50,330)');

// 取 Y 軸比例尺
const scaleY = d3
  .scaleLinear()
  .domain([d3.max(report.map(item => item.quantity)), 0])
  .range([0, 300]);

// 顯示 Y 坐標軸
const axisY = d3.axisLeft(scaleY);
chart
  .append('g')
  .call(axisY)
  .attr('transform', 'translate(50,30)');

// 定義線段
const line = d3
  .line()
  .x(item => scaleX(item.month))
  .y(item => scaleY(item.quantity))
  .curve(d3.curveBasis);

// 將線段繪製出來
group
  .append('path')
  .attr('d', line(report))
  .attr('transform', 'translate(50,30)')
  .attr('stroke', 'black')
  .attr('stroke-width', 1)
  .attr('fill', 'none');

折線圖範例

結語

這篇畫了三種基本的圖表,分別為長條圖、散佈圖與折線圖,簡單的使用了 D3 提供的方法來繪製,其實畫起來並不難,D3 幫我們處理掉許多底層的部分,接下來只要夠了解 SVG 就能夠輕易的畫出漂亮的圖了,那就祝大家畫圖表愉快拉~


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言