D3.js - 图
D3.js - SVG 坐标空间和基本图表绘制
Section titled “D3.js - SVG 坐标空间和基本图表绘制”理解 SVG 坐标空间(SVG coordinate space)对于使用 D3.js 绘图至关重要。虽然数学上的笛卡尔坐标系(Cartesian coordinates)通常将 (0,0) 设置在左下角且 Y 轴向上递增,但 SVG 的坐标系统有所不同。
SVG 坐标空间
Section titled “SVG 坐标空间”SVG 坐标空间有两个主要特点:
- 原点 (0,0) 位于 SVG 视口(viewport)的左上角。
- Y 坐标向下递增,而 X 坐标向右递增(类似于数学图表)。
因此,如果你在 cx=30, cy=30 绘制一个圆,它将位于 SVG 元素左上角向右 30 个单位、向下 30 个单位的位置。
// 创建一个带有边框的 SVG 容器来可视化其边界const svgContainer = d3.select("body") .append("svg") .attr("width", 200) .attr("height", 200) .style("border", "1px solid black");
// 在这个 SVG 内绘制一个圆 (30,30)svgContainer.append("circle") .attr("cx", 30) .attr("cy", 30) .attr("r", 20) .style("fill", "steelblue");示例:创建一个折线图
Section titled “示例:创建一个折线图”折线图是一种常见的可视化类型,用于展示随时间变化的趋势或连续变量。它通常有 X 轴和 Y 轴,数据点由线条连接。
让我们创建一个展示虚构人口增长的折线图。首先,准备一个 population-data.csv 文件:
year,population2006,402008,452010,482012,512014,532016,572017,62以下是绘制折线图的步骤:
步骤 1:设置 HTML 和样式
Section titled “步骤 1:设置 HTML 和样式”创建一个 HTML 文件并添加基本的线条和坐标轴样式。
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>D3 Line Graph</title> <script src="https://d3js.org/d3.v7.min.js"></script> <style> .line { fill: none; stroke: steelblue; stroke-width: 2px; } .axis path, .axis line { fill: none; stroke: #000; shape-rendering: crispEdges; } .axis text { font-family: sans-serif; font-size: 10px; } </style></head><body> <svg id="line-chart" width="600" height="400"></svg> <script> // D3 代码将在这里 </script></body></html>步骤 2:定义外边距和尺寸
Section titled “步骤 2:定义外边距和尺寸”使用外边距约定(margin convention)为坐标轴创建空间。
const margin = {top: 20, right: 30, bottom: 40, left: 50};const width = 600 - margin.left - margin.right;const height = 400 - margin.top - margin.bottom;
const svg = d3.select("#line-chart") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") // 添加一个组元素 (group) .attr("transform", `translate(${margin.left},${margin.top})`); // 应用偏移转换 (translate transform)步骤 3:定义比例尺
Section titled “步骤 3:定义比例尺”比例尺(Scales)将数据值映射到像素位置。对于 ‘year’,我们将使用 d3.scaleTime(假设年份被解析为日期)。对于 ‘population’,使用 d3.scaleLinear。
const xScale = d3.scaleTime().range([0, width]);const yScale = d3.scaleLinear().range([height, 0]); // 注意:对于 Y 轴映射,0 位于底部步骤 4:定义折线生成器
Section titled “步骤 4:定义折线生成器”d3.line() 将为我们的折线生成 path 字符串。
const lineGenerator = d3.line() .x(d => xScale(d.year)) .y(d => yScale(d.population));步骤 5:加载和处理数据
Section titled “步骤 5:加载和处理数据”加载 population-data.csv 并解析数据类型。
d3.csv("population-data.csv", d => { // 自定义行访问器以解析数据 return { year: new Date(+d.year, 0, 1), // 将年份转换为 Date 对象 (该年份的 1 月 1 日) population: +d.population // 将人口转换为数字 };}).then(data => { // 步骤 6-9 将在此 .then() 块内 console.log(data);
// 步骤 6:设置比例尺的定义域 (Domain) xScale.domain(d3.extent(data, d => d.year)); yScale.domain([0, d3.max(data, d => d.population)]);
// 步骤 7:绘制折线 svg.append("path") .datum(data) .attr("class", "line") .attr("d", lineGenerator);
// 步骤 8:添加 X 轴 svg.append("g") .attr("class", "axis x-axis") .attr("transform", `translate(0,${height})`) .call(d3.axisBottom(xScale).ticks(d3.timeYear.every(2)).tickFormat(d3.timeFormat("%Y")));
// X 轴标签 svg.append("text") .attr("transform", `translate(${width / 2}, ${height + margin.bottom - 5})`) .style("text-anchor", "middle") .text("年份");
// 步骤 9:添加 Y 轴 svg.append("g") .attr("class", "axis y-axis") .call(d3.axisLeft(yScale));
// Y 轴标签 svg.append("text") .attr("transform", "rotate(-90)") .attr("y", 0 - margin.left) .attr("x", 0 - (height / 2)) .attr("dy", "1em") .style("text-anchor", "middle") .text("人口");
}).catch(error => { console.error("加载或处理数据出错:", error);});确保 population-data.csv 与你的 HTML 文件位于同一目录中,或调整路径。打开 HTML 文件后,你将看到折线图。
使用的关键概念
Section titled “使用的关键概念”- SVG 视口与分组: 使用
<svg>和<g>元素,以及transform属性。 - 外边距: 用于图表布局的外边距约定。
- 比例尺 (
d3-scale): 将数据值映射到视觉表示(像素)。d3.scaleTime用于基于时间的数据,d3.scaleLinear用于定量数据。 - 坐标轴 (
d3-axis): 从比例尺生成人类可读的坐标轴标签和刻度。 - 图形 (
d3-shape): 使用d3.line()从点数组生成 SVG path 数据。 - 数据加载 (
d3-fetch): 异步加载和解析 CSV 数据。
这个例子为构建更复杂的图表提供了基础。你可以在此基础上扩展,添加工具提示、交互性、多条折线等。有关 SVG 的详细信息,请参阅 MDN SVG 教程。