Skip to content

D3.js - 图

D3.js - SVG 坐标空间和基本图表绘制

Section titled “D3.js - SVG 坐标空间和基本图表绘制”

理解 SVG 坐标空间(SVG coordinate space)对于使用 D3.js 绘图至关重要。虽然数学上的笛卡尔坐标系(Cartesian coordinates)通常将 (0,0) 设置在左下角且 Y 轴向上递增,但 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");

折线图是一种常见的可视化类型,用于展示随时间变化的趋势或连续变量。它通常有 X 轴和 Y 轴,数据点由线条连接。

让我们创建一个展示虚构人口增长的折线图。首先,准备一个 population-data.csv 文件:

year,population
2006,40
2008,45
2010,48
2012,51
2014,53
2016,57
2017,62

以下是绘制折线图的步骤:

创建一个 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>

使用外边距约定(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)

比例尺(Scales)将数据值映射到像素位置。对于 ‘year’,我们将使用 d3.scaleTime(假设年份被解析为日期)。对于 ‘population’,使用 d3.scaleLinear。

const xScale = d3.scaleTime().range([0, width]);
const yScale = d3.scaleLinear().range([height, 0]); // 注意:对于 Y 轴映射,0 位于底部

d3.line() 将为我们的折线生成 path 字符串。

const lineGenerator = d3.line()
.x(d => xScale(d.year))
.y(d => yScale(d.population));

加载 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 文件后,你将看到折线图。

  • SVG 视口与分组: 使用 <svg> 和 <g> 元素,以及 transform 属性。
  • 外边距: 用于图表布局的外边距约定。
  • 比例尺 (d3-scale): 将数据值映射到视觉表示(像素)。d3.scaleTime 用于基于时间的数据,d3.scaleLinear 用于定量数据。
  • 坐标轴 (d3-axis): 从比例尺生成人类可读的坐标轴标签和刻度。
  • 图形 (d3-shape): 使用 d3.line() 从点数组生成 SVG path 数据。
  • 数据加载 (d3-fetch): 异步加载和解析 CSV 数据。

这个例子为构建更复杂的图表提供了基础。你可以在此基础上扩展,添加工具提示、交互性、多条折线等。有关 SVG 的详细信息,请参阅 MDN SVG 教程。