D3.js - 绘制图表
D3.js - 绘制基础图表
Section titled “D3.js - 绘制基础图表”通过操作 SVG 元素,D3.js 在创建各种静态和交互式图表方面非常灵活。常见的图表类型包括:
- 柱状图 (Bar Charts)
- 散点图 (Scatter Plots,通常使用圆形)
- 饼图 (Pie Charts) 和环形图 (Donut Charts)
- 折线图 (Line Charts)
- 气泡图 (Bubble Charts) 等。
本章将指导您使用 D3.js 和 SVG 绘制一些基本的图表类型。
柱状图 (Bar Chart)
Section titled “柱状图 (Bar Chart)”柱状图 (Bar Charts) 用于显示和比较分类数据,其中每个柱子的长度或高度与其值成比例。x 轴通常表示类别,而 y 轴(对于水平柱状图则是 x 轴)表示测量值。
绘制水平柱状图
Section titled “绘制水平柱状图”让我们创建一个简单的水平柱状图。我们将使用 SVG 的 <rect> 元素作为柱子,使用 <text> 元素作为标签。
步骤 1: 定义 HTML 结构和样式。创建一个 HTML 文件 (例如,barchart.html)。
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>D3 Bar Chart</title> <script src="https://d3js.org/d3.v7.min.js"></script> <style> .bar-chart-container svg { border: 1px solid #ccc; /* Optional: to see SVG boundary */ } .bar-chart-container .bar rect { fill: steelblue; } .bar-chart-container .bar rect:hover { fill: orange; } .bar-chart-container .bar text { fill: black; font: 10px sans-serif; text-anchor: start; /* For labels next to bars */ dominant-baseline: middle; } </style></head><body> <div class="bar-chart-container"> <h3>Simple Horizontal Bar Chart</h3> <div id="chart-area"></div> </div> <script> // D3 code will go here </script></body></html>步骤 2: JavaScript - 定义数据和图表参数。
// 在 barchart.html 文件底部 <script> 标签内const data = [10, 5, 12, 15, 8, 10];
const chartWidth = 400;const barHeight = 20;const barPadding = 5; // 柱子之间的间距const labelOffset = 5; // 文本标签与柱子末端的间距const chartHeight = data.length * (barHeight + barPadding);
// 创建一个用于计算柱子宽度的比例尺 (将数据值映射到像素)// 注意:对于更健壮的图表,请使用 d3.scaleLinear().domain([0, d3.max(data)]).range([0, chartWidth - labelOffsetArea])// 为简单起见,这里我们将使用手动计算的 scaleFactor 作为非常基础的示例。const scaleFactor = (chartWidth - 50) / d3.max(data); // 50 是为标签预留的任意空间一种更健壮的缩放方法是使用 d3.scaleLinear():const xScale = d3.scaleLinear().domain([0, d3.max(data)]).range([0, chartWidth - 50]); 然后柱子的宽度将是 d => xScale(d)。
步骤 3: 创建 SVG 和柱子组 (group)。
const svg = d3.select("#chart-area") .append("svg") .attr("width", chartWidth) .attr("height", chartHeight);
const bars = svg.selectAll(".bar") .data(data) .enter() .append("g") .attr("class", "bar") .attr("transform", (d, i) => `translate(0, ${i * (barHeight + barPadding)})`);步骤 4: 添加 <rect> 元素作为柱子。
bars.append("rect") .attr("width", d => d * scaleFactor) // 如果使用 d3.scaleLinear,则改为 xScale(d) .attr("height", barHeight);步骤 5: 添加 <text> 元素作为标签。
bars.append("text") .attr("x", d => (d * scaleFactor) + labelOffset) // 如果使用 d3.scaleLinear,则改为 xScale(d) + labelOffset .attr("y", barHeight / 2) // .attr("dy", ".35em") // dominant-baseline: middle 通常效果更好 .text(d => d);完整的 barchart.html 文件 (包含脚本部分):
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>D3 Bar Chart</title> <script src="https://d3js.org/d3.v7.min.js"></script> <style> .bar-chart-container svg { border: 1px solid #ccc; } .bar-chart-container .bar rect { fill: steelblue; } .bar-chart-container .bar rect:hover { fill: orange; } .bar-chart-container .bar text { fill: black; font: 10px sans-serif; text-anchor: start; dominant-baseline: middle; } </style></head><body> <div class="bar-chart-container"> <h3>Simple Horizontal Bar Chart</h3> <div id="chart-area"></div> </div> <script> const data = [10, 5, 12, 15, 8, 10]; const chartWidth = 400; const barHeight = 20; const barPadding = 5; const labelOffset = 5; const chartHeight = data.length * (barHeight + barPadding); const scaleFactor = (chartWidth - 50) / d3.max(data);
const svg = d3.select("#chart-area") .append("svg") .attr("width", chartWidth) .attr("height", chartHeight);
const bars = svg.selectAll(".bar") .data(data) .enter() .append("g") .attr("class", "bar") .attr("transform", (d, i) => `translate(0, ${i * (barHeight + barPadding)})`);
bars.append("rect") .attr("width", d => d * scaleFactor) .attr("height", barHeight);
bars.append("text") .attr("x", d => (d * scaleFactor) + labelOffset) .attr("y", barHeight / 2) .text(d => d); </script></body></html>在浏览器中打开此文件时,将显示一个简单的水平柱状图。每个柱子的长度对应其数据值,数据值会打印在柱子旁边。
圆形图 (简单的散点图点)
Section titled “圆形图 (简单的散点图点)”此示例演示了如何绘制圆形,其半径基于数据值,并水平排列。
步骤 1: HTML 结构 (例如,circlechart.html)。
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>D3 Circle Chart</title> <script src="https://d3js.org/d3.v7.min.js"></script> <style> #circle-chart-area svg { border: 1px solid #ccc; } #circle-chart-area .data-circle { stroke: black; stroke-width: 1px; } #circle-chart-area .data-label { font: 10px sans-serif; text-anchor: middle; } </style></head><body> <h3>Simple Circle Chart</h3> <div id="circle-chart-area"></div> <script> // D3 code here </script></body></html>步骤 2: JavaScript - 数据和参数。
// 在 <script> 标签内const circleData = [10, 20, 30, 15, 25];const circleColors = ['green', 'purple', 'yellow', 'blue', 'red'];
const svgWidth = 500;const svgHeight = 150; // 容纳最大圆形和标签所需的高度const circleYPosition = svgHeight / 2; // 垂直居中圆形const circleSpacing = 100; // 圆形中心点之间的水平间距步骤 3: 创建 SVG 和每个圆形-标签对的组 (group)。
const svgCircles = d3.select("#circle-chart-area") .append("svg") .attr("width", svgWidth) .attr("height", svgHeight);
const circleGroups = svgCircles.selectAll(".circle-group") .data(circleData) .enter() .append("g") .attr("class", "circle-group") .attr("transform", (d, i) => `translate(${i * circleSpacing + 50}, 0);`); // +50 作为初始内边距步骤 4: 添加 <circle> 元素。
circleGroups.append("circle") .attr("class", "data-circle") .attr("cy", circleYPosition) .attr("r", d => d * 1.5) // 基于数据并经过缩放的半径 .attr("fill", (d, i) => circleColors[i % circleColors.length]);步骤 5: 添加 <text> 标签。
circleGroups.append("text") .attr("class", "data-label") .attr("y", circleYPosition) // .attr("dy", d => (d * 1.5) + 15) // 将标签置于圆形下方,根据需要调整 .attr("dy", ".35em") // 如果不偏移到圆形下方,则在 cy 坐标上居中文本 .text(d => d);完整的 circlechart.html 文件中的脚本部分:
const circleData = [10, 20, 30, 15, 25]; const circleColors = ['green', 'purple', 'yellow', 'blue', 'red']; const svgWidth = 500; const svgHeight = 150; const circleYPosition = svgHeight / 2; const circleSpacing = (svgWidth - 50) / circleData.length; // Dynamic spacing
const svgCircles = d3.select("#circle-chart-area") .append("svg") .attr("width", svgWidth) .attr("height", svgHeight);
const circleGroups = svgCircles.selectAll(".circle-group") .data(circleData) .enter() .append("g") .attr("class", "circle-group") .attr("transform", (d, i) => `translate(${i * circleSpacing + circleSpacing/2 + 25}, 0)`); // Center in allocated space, +25 for initial margin
circleGroups.append("circle") .attr("class", "data-circle") .attr("cy", circleYPosition) .attr("r", d => d * 1.5) .attr("fill", (d, i) => circleColors[i % circleColors.length]);
circleGroups.append("text") .attr("class", "data-label") .attr("y", circleYPosition) .attr("dy", ".35em") .text(d => d);这将渲染一系列彩色圆形,每个圆形的半径与其数据值成比例,数据值本身则打印在圆形中心。
饼图 (Pie Chart)
Section titled “饼图 (Pie Chart)”饼图 (Pie Charts) 将数据表示为圆形的切片,每个切片的角度与其相对于总值的比例成正比。D3 提供了 d3.pie() 用于计算角度,以及 d3.arc() 用于为切片生成 SVG path 数据。
步骤 1: HTML 结构和样式 (例如,piechart.html)。
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>D3 Pie Chart</title> <script src="https://d3js.org/d3.v7.min.js"></script> <style> #pie-chart-area svg { border: 1px solid #ccc; } .pie-slice path { stroke: #fff; /* White border between slices */ stroke-width: 1px; } .pie-label text { font: 12px sans-serif; text-anchor: middle; fill: white; } .chart-title { font: bold 16px sans-serif; text-anchor: middle; } </style></head><body> <h3>Population Distribution (Pie Chart)</h3> <div id="pie-chart-area"></div> <script> // D3 code for pie chart </script></body></html>步骤 2: 数据和图表参数。我们将使用一个示例数据集 (例如,人口数据)。在实际场景中,您可能使用 d3.csv() 或 d3.json() 从 CSV 或 JSON 文件加载数据,这些函数返回 Promise。
// 在 <script> 标签内const populationData = [ { state: "UP", percent: 20.00 }, { state: "MH", percent: 15.00 }, { state: "BR", percent: 12.50 }, { state: "WB", percent: 10.00 }, { state: "MP", percent: 8.00 }, { state: "Other", percent: 34.50 }];
const pieWidth = 400;const pieHeight = 400;const radius = Math.min(pieWidth, pieHeight) / 2 - 10; // -10 作为外边距步骤 3: 创建 SVG 和一个用于饼图的组 (group) 元素,并居中。
const svgPie = d3.select("#pie-chart-area") .append("svg") .attr("width", pieWidth) .attr("height", pieHeight) .append("g") // 用于居中饼图的组 .attr("transform", `translate(${pieWidth / 2}, ${pieHeight / 2})`);步骤 4: 颜色比例尺和饼图生成器。
const colorScale = d3.scaleOrdinal(d3.schemeCategory10); // 内置颜色方案
const pieGenerator = d3.pie() .value(d => d.percent) // 访问器,用于获取值以确定切片大小 .sort(null); // 可选:保留原始数据顺序或按值排序步骤 5: 用于绘制切片的圆弧生成器 (Arc Generator)。
const arcGenerator = d3.arc() .innerRadius(0) // 饼图内半径为 0;环形图则 > 0 .outerRadius(radius);步骤 6: 用于定位标签的圆弧生成器 (可选,用于将标签放置在切片内部/外部)。
const labelArcGenerator = d3.arc() .innerRadius(radius * 0.6) // 将标签放置在切片内部 .outerRadius(radius * 0.6);步骤 7: 绑定数据并绘制切片。
const arcs = svgPie.selectAll(".pie-slice") .data(pieGenerator(populationData)) .enter() .append("g") .attr("class", "pie-slice");
arcs.append("path") .attr("d", arcGenerator) .attr("fill", d => colorScale(d.data.state));步骤 8: 为切片添加标签。
arcs.append("text") .attr("class", "pie-label") .attr("transform", d => `translate(${labelArcGenerator.centroid(d)})`) // 使用 labelArc 的质心 (centroid) 定位 .text(d => `${d.data.state} (${d.data.percent}%)`) .each(function(d) { // 如果切片太小则隐藏标签 const angle = d.endAngle - d.startAngle; if (angle < 0.25) { // 角度阈值(以弧度为单位),根据需要调整 d3.select(this).remove(); } });步骤 9: 添加标题 (可选)。
d3.select("#pie-chart-area svg") // 再次选择主 SVG 元素 .append("text") .attr("class", "chart-title") .attr("x", pieWidth / 2) .attr("y", 20) // 定位在顶部 .text("State Population Distribution");完整的 piechart.html 文件中的脚本部分将结合这些 JavaScript 代码片段。查看时,它会显示一个表示人口分布的饼图,切片上带有标签。
这些示例提供了基础。实际图表通常涉及更复杂的比例尺 (scales)、坐标轴 (axes)、图例 (legends) 和交互 (interactions),所有这些 D3.js 都可以处理。