程式碼
//html
<div class="expensesChart"></div>
//js
// 建立svg
const currentWidth = parseInt(
d3.select(".expensesChart").style("width")
),
height = 400,
margin = 40;
const svg = d3
.select(".expensesChart")
.append("svg")
.attr("width", currentWidth)
.attr("height", height);
const data = [
{ item: "交通", data: 3000 },
{ item: "房租", data: 12000 },
{ item: "日常用品", data: 1400 },
{ item: "吃飯", data: 4000 },
{ item: "交際應酬", data: 2400 },
];
// 建立圓餅集合標籤g
svg
.append("g")
.attr("class", "slices")
.attr("transform",
`translate(${currentWidth / 2}, ${height / 2})`
);
// 設定顏色
const color = d3.scaleOrdinal().range(d3.schemeSet2);
// 設定圓餅半徑
const radius = Math.min(currentWidth, height) / 2 - margin;
// 設定圓餅建構函式
const piechartGenerator = d3.pie().value((d) => d.data);
// 用innerRadius 跟 outerRadius 設定圓餅內圈外圈的半徑
const arc = d3
.arc()
.innerRadius(0)
.outerRadius(radius)
.padAngle(0);
const outerArc = d3
.arc()
.outerRadius(radius * 0.9)
.innerRadius(radius * 0.9);
// 圓餅圖建構函式帶入資料
const pieChartData = piechartGenerator(data);
// 建立pie
const expensesChart = svg
.select(".slices")
.selectAll("path")
.data(pieChartData)
.enter();
// 綁定圓弧路徑
expensesChart
.append("path")
.attr("d", arc)
.attr("class", "arc")
.attr("fill", color)
.attr("stroke", "#fff")
.style("stroke-width", "3px")
.style("opacity", 1);
// 加上每個區塊的數字標示
// 計算每塊資料的百分占比,先用 d3.sum 加總全部資料,再將資料一一除上總數
const total = d3.sum(data, (d) => d.data);
data.forEach((i) => {
i.percentage = Math.round((i.data / total) * 100);
});
// 調整數字標示的位置
const textArc = d3
.arc()
.innerRadius(radius)
.outerRadius(radius - 10);
// 綁定數字標籤位置
expensesChart
.append("text")
.attr("transform", (d) => `translate(${textArc.centroid(d)})`)
.text((d) => d.data.item + d.data.percentage + "%")
.style("text-anchor", "middle")
.style("font-size", 16)
.style("fill", "black");
程式碼
const drawExpensesChartHover = ()=>{
const data = [
{ item: "交通", data: 3000 },
{ item: "房租", data: 12000 },
{ item: "日常用品", data: 1400 },
{ item: "吃飯", data: 4000 },
{ item: "交際應酬", data: 2400 },
];
// svg
const currentWidth = parseInt(
d3.select(".expensesChartHover").style("width")
),
height = 400,
margin = 40;
// 先設定 svg 大小
const svg = d3
.select(".expensesChartHover")
.append("svg")
.attr("width", currentWidth)
.attr("height", height);
// 圓餅集合標籤
svg
.append("g")
.attr("class", "slices")
.attr("transform", `translate(${currentWidth / 2}, ${height / 2})`);
// 設定顏色
const color = d3.scaleOrdinal().range(d3.schemeSet2);
// 設定圓餅半徑
const radius = Math.min(currentWidth, height) / 2 - margin;
// 將資料綁定到圓餅圖上:
const piechartGenerator = d3.pie().value((d) => d.data);
// innerRadius 跟 outerRadius 設定圓餅內圈外圈的大小 radius
const arc = d3.arc().innerRadius(0).outerRadius(radius).padAngle(0), // 圓餅間距
outerArc = d3
.arc()
.outerRadius(radius * 0.9)
.innerRadius(radius * 0.9);
// 圓餅圖建構函式帶入資料
const pieChartData = piechartGenerator(data);
// 建立pie
const expensesChart = svg
.select(".slices")
.selectAll("path")
.data(pieChartData)
.enter();
// 綁定圓弧路徑
expensesChart
.append("path")
.attr("d", arc)
.attr("class", "arc")
.attr("fill", color)
.attr("stroke", "#fff")
.style("stroke-width", "3px")
.style("opacity", 1);
// 加上每個區塊的數字標示 =========================
// 計算每塊資料的百分占比,先用 d3.sum 加總全部資料,再將資料一一除上總數
const total = d3.sum(data, (d) => d.data);
data.forEach((i) => {
i.percentage = Math.round((i.data / total) * 100);
});
// 調整數字標示的位置
const textArc = d3
.arc()
.innerRadius(radius)
.outerRadius(radius - 10);
// 綁定數字標籤位置
expensesChart
.append("text")
.attr("transform", (d) => `translate(${textArc.centroid(d)})`)
.text((d) => d.data.item + d.data.percentage + "%")
.style("text-anchor", "middle")
.style("font-size", 16)
.style("fill", "black");
// 滑鼠互動
d3.selectAll('.arc')
.style('cursor', 'pointer')
.on('mouseover',(d)=>{
d3.select(d.target)
.transition()
.duration(500)
.style("filter", "drop-shadow(2px 4px 6px black)")
.style('transform', 'scale(1.1)')
})
.on('mouseleave', (d)=>{
d3.select(d.target)
.transition()
.duration(500)
.style("filter", "drop-shadow(0 0 0 black)")
.style('transform', 'scale(1)')
})
}
drawExpensesChartHover();
程式碼
//html
<div class="d-flex justify-content-center">
<button class="px-4 btn btn-warning me-2">
1月
</button>
<button class="px-4 btn btn-warning ms-2">
2月
</button>
</div>
<div class="pieChartTransition"></div>
//JS
// pie svg
const pieWidth = parseInt(
d3.select(".pieChartTransition").style("width")
),
pieHeight = 400,
pieMargin = 40;
const pieSvg = d3
.select(".pieChartTransition")
.append("svg")
.attr("width", pieWidth)
.attr("height", pieHeight);
// 切換月份的按鈕
const JanuaryBtn = d3.select('.januaryBtn')
const FeburaryBtn = d3.select('.feburaryBtn')
JanuaryBtn.on('click', (e,d)=>{
// 按鈕切換
JanuaryBtn.style('background-color', '#e09500');
FeburaryBtn.style('background-color', '#ffc107');
updatePieData(pieDataJanuary)
});
FeburaryBtn.on('click', (e,d)=>{
// 按鈕切換
FeburaryBtn.style('background-color', '#e09500');
JanuaryBtn.style('background-color', '#ffc107');
updatePieData(pieDataFeburary)
})
// 建立不同月份的資料集
const pieDataJanuary = [
{ item: "交通", data: 3000 },
{ item: "房租", data: 12000 },
{ item: "日常用品", data: 1400 },
{ item: "吃飯", data: 4000 },
{ item: "交際應酬", data: 2400 },
];
const pieDataFeburary = [
{ item: "交通", data: 1600 },
{ item: "房租", data: 12000 },
{ item: "日常用品", data: 2000 },
{ item: "吃飯", data: 2000 },
{ item: "交際應酬", data: 1600 },
];
// 建立圓餅集合標籤g
const pie = pieSvg
.append("g")
.attr("transform",
`translate(${pieWidth / 2}, ${pieHeight / 2})`
);
// 設定顏色
const pieColor = d3
.scaleOrdinal()
.domain(["交通", "房租", "日常用品", "吃飯", "交際應酬"])
.range(d3.schemeSet2);
// 設定圓餅半徑
const radius = Math.min(pieWidth, pieHeight) / 2 - pieMargin;
// 設定更新資料的方法
const updatePieData = (data) => {
// 將資料綁定到圓餅圖上
const piechartGenerator = d3
.pie()
.value((d) => d.data)
// 固定圓餅圖的項目排序,確保資料抽換時群組順序保持一致
.sort((a, b) => d3.ascending(a.key, b.key));
// innerRadius 跟 outerRadius 設定圓餅內圈外圈的半徑
const arc = d3
.arc()
.innerRadius(0)
.outerRadius(radius)
.padAngle(0);
const outerArc = d3
.arc()
.outerRadius(radius * 0.9)
.innerRadius(radius * 0.9);
// 設定圓餅圖建構函式
const pieChartData = piechartGenerator(data);
// 建立pie
const expensesChart = pie.selectAll("path").data(pieChartData);
// 將資料綁定圓弧路徑
expensesChart
.join("path")
// 資料變化動畫
.transition()
.duration(1000)
.attr("class", "slice")
.attr("d", arc)
.attr("fill", (d) => pieColor(d.data.item))
.attr("stroke", "#fff")
.style("stroke-width", "3px")
.style("opacity", 1);
// 加上每個區塊的數字標示
// 計算每塊資料的百分占比,先用 d3.sum 加總全部資料,再將資料一一除上總數
const total = d3.sum(data, (d) => d.data);
data.forEach((i) => {
i.percentage = Math.round((i.data / total) * 100);
});
// 調整數字標示的位置
const textArc = d3
.arc()
.innerRadius(radius)
.outerRadius(radius + 20);
// 綁定數字標籤並設定位置
const textLabel = pie
.selectAll("text")
.data(pieChartData)
.join("text")
.text((d) => d.data.item + d.data.percentage + "%")
.transition()
.duration(1000)
.attr("transform", (d) => `translate(${textArc.centroid(d)})`)
.style("text-anchor", "middle")
.style("font-size", 16)
.style("fill", "black");
};
updatePieData(pieDataJanuary);