本篇大綱:範例一、範例二
昨天我們講完了基礎圖表的章節,學會圓餅圖、散點圖、直條圖跟折線圖等等基礎常見的圖表,今天我們要進到進階的圖表啦!進階的圖表主要是較少用到或是寫法較困難的圖表,有些進階圖表是從基礎圖表演化而來,例如:甜甜圈圖、氣泡圖等;有些則是由完全不同的方法撰寫而成的圖表,例如階層圖、地圖等等。我們就先從延伸而來的圖表開始講起吧!今天~就讓我們來看看從圓餅圖延伸而來的甜甜圈圖
!
我們今天的範例有兩個,第一個是基礎的甜甜圈圖:
第二個是加上標示線段的甜甜圈圖
甜甜圈圖其實是從圓餅圖延伸而來,它的關鍵就在於設定圓餅內圈的半徑
。使用 d3.arc
設定圓弧的形狀時,可以分別設定內弧跟外弧的半徑
// Creating arc
const arc = d3.arc()
.innerRadius(50)
.outerRadius(100);
如果把內弧半徑設為0的話,就會形成圓餅圖;但如果內弧半徑設定成不同數值,就能任意調整內圈半徑,進而形成甜甜圈圖。現在我們就來看看範例一要怎麼做吧!
我們先來畫一個基本的甜甜圈圖加上資訊。首先,一樣先取資料並建立svg
// html
<div class="basicDonut"></div>
// js
const data = [
{ "value": 1, "property": "p1" },
{ "value": 2, "property": "p2" },
{ "value": 3, "property": "p3" },
{ "value": 4, "property": "p4" },
{ "value": 5, "property": "p5" },
{ "value": 6, "property": "p6" }
]
// 建立svg
const svg = d3.select(".basicDonut")
.append('svg')
.attr('width', 300)
.attr('height', 300);
再來,我們用 d3.pie( )
方法來建立生成圓餅圖的方法,然後用 d3.arc( )
繪製弧度
// 用 pie()建立圓餅圖 generator
const pie = d3.pie()
.value((d) => { return d.value })
(data);
// 建立圓弧
const arc = d3.arc()
.innerRadius(50)
.outerRadius(100);
接著就是簡單的綁定資料並加上路徑啦
// 綁定資料
const arcs = svg.append("g")
.attr("transform", "translate(150, 120)")
.selectAll("arc")
.data(pie)
.enter()
.append("g");
// 加上路徑
arcs.append("path")
.attr("fill", (data, i) => {
return d3.schemeSet2[i];
})
.attr("d", arc);
如果想要加上資料標示,就一樣用 append.text的 方式去處理
// 加上內部文字標示
arcs.append("text")
.attr("transform", (d) => {
return "translate(" +
arc.centroid(d) + ")";
})
.text(function (d) {
return d.value;
});
這樣就完成啦~寫起來是不是幾乎跟圓餅圖一模一樣呢?
我們接著再來看看範例二,這個就比較有趣~是加上外拉的標籤線段來標示每個區塊的資料,並在內圈放上圖表的主題
首先,我們一樣先把甜甜圈圖建立出來
// html
<div class="advancedDonut"></div>
// js
// 建立資料
const data = [{city:'台北', data:30},{city:'新北', data:45},
{city:'台中', data:9},{city:'嘉義', data:67},{city:'台南', data:22}]
// RWD 的svg 寬高
const svgWidth = parseInt(d3.select('.basicDonut').style('width')),
svgHeight = svgWidth*0.8,
margin = 60
var svg = d3.select(".advancedDonut").append("svg")
.attr("width", svgWidth)
.attr("height", svgHeight)
.append("g");
// 設定圖表寬高與圓弧半徑
const width = svgWidth-margin*2;
const height = svgHeight-margin*2;
// radius設定圓餅圖的圓弧大小,是區域的一半
const radius = Math.min(width, height)/2;
// 設定顏色
const color = d3.scaleOrdinal()
.range(["#4BEFCF","#2c9af7","#F96262", '#910842', '#b054e5']);
// 圓餅、線段、標籤
svg.append("g")
.attr("class", "slices");
svg.append("g")
.attr("class", "labels");
svg.append("g")
.attr("class", "lines");
// 建立圓餅圖
const pie = d3.pie().sort(null).value(d => d.data);
const arc = d3.arc().innerRadius(radius*0.3).outerRadius(radius*0.6);
// 設定弧度
const outerArc = d3.arc()
.outerRadius(radius * 0.9)
.innerRadius(radius * 0.9);
// 建立甜甜圈圖
svg.attr("transform", "translate(" + svgWidth / 2 + "," + svgHeight / 2 + ")");
svg.selectAll('path')
.data(pie(data))
.enter()
.append('path')
.attr('d', arc)
.attr('fill', (d,i)=> color(i));
再來建立標示的線段跟文字標籤。這邊的關鍵是線段跟標籤的位置,我們要根據外弧的位置去設定,因此會用outerArc.centroid
去處理
// 建立線段跟標示
svg.append('g').classed('labels',true);
svg.append('g').classed('lines',true);
// 設定線段
const polyline = svg.select('.lines')
.selectAll('polyline')
.data(pie(data))
.enter()
.append('polyline')
// 這邊是關鍵,要根據外弧的位置去調整線段位置
.attr('points', function(d) {
const pos = outerArc.centroid(d);
pos[0] = radius * 0.95 * (midAngle(d) < Math.PI ? 1 : -1);
return [arc.centroid(d), outerArc.centroid(d), pos]
});
// 設定文字標籤
const label = svg.select('.labels').selectAll('text')
.data(pie(data))
.enter()
.append('text')
.attr('dy', '.35em')
.html(d => `${d.data.city}:${d.data.data}萬度`)
// 這邊是關鍵,要根據外弧的位置去調整標籤位置
.attr('transform', function(d) {
const pos = outerArc.centroid(d);
pos[0] = radius * 0.95 * (midAngle(d) < Math.PI ? 1 : -1);
return 'translate(' + pos + ')';
})
.style('text-anchor', function(d) {
return (midAngle(d)) < Math.PI ? 'start' : 'end';
});
// 綁定文字標籤要顯示的內容
svg.append('text')
.attr('class', 'toolCircle')
.attr('dy', 0)
.html('用電量')
.style('font-size', '.9em')
.style('text-anchor', 'middle');
function midAngle(d) { return d.startAngle + (d.endAngle - d.startAngle) / 2; }
然後我們可以將線段跟文字標籤設定一些樣式
/*資料軸線*/
.advancedDonut polyline {
opacity: .3;
stroke: black;
stroke-width: 2px;
fill: none;
}
/* 字體加粗*/
.labelName tspan {
font-style: normal;
font-weight: 700;
}
/* 字形 */
.labelName {
font-size: 0.9em;
font-style: italic;
}
這樣就完成啦!是不是也沒多困難呢?今天的甜甜圈圖就講到這邊~明天要來講氣泡圖囉!
最後附上本章的程式碼:想看完整程式碼的請上 Github,想直接操作圖表的則去 Github Page 吧!請自行取用~