跳转至

D3.js 数据可视化 — 用数据驱动 DOM,画出任意交互图表


一句话说明

D3(Data-Driven Documents)是一个把数据绑定到 HTML/SVG 元素上的 JavaScript 库,所有图形都由你自己用代码"画"出来,灵活度极高,适合定制化科研图表。


安装与配置

# 通过 npm 安装 D3 v7(当前稳定版)
npm install d3

# 或者直接在 HTML 里用 CDN 引入(零配置)
# <script src="https://cdn.jsdelivr.net/npm/d3@7"></script>

# 查看已安装版本
node -e "console.log(require('d3/package.json').version)"

核心用法

import * as d3 from 'd3';  // 导入整个 D3 库

// ── 1. 选择元素(类似 jQuery 的 $ 选择器)──
const svg = d3.select('#chart')          // 选中 id=chart 的 SVG 元素
  .attr('width', 600)                    // 设置宽度
  .attr('height', 400);                  // 设置高度

// ── 2. 绑定数据(D3 的核心思想)──
const data = [10, 30, 50, 20, 80];      // 原始数据数组

const bars = svg.selectAll('rect')       // 选中所有 rect(可以不存在)
  .data(data)                            // 把数据与元素一一绑定
  .join('rect');                         // 有数据就创建 rect,多余的删掉

// ── 3. 比例尺(把数据映射到像素)──
const xScale = d3.scaleBand()           // 分组比例尺(适合柱状图)
  .domain(d3.range(data.length))        // 输入域:0,1,2,3,4
  .range([0, 600])                      // 输出域:像素范围
  .padding(0.2);                        // 柱子间距

const yScale = d3.scaleLinear()         // 线性比例尺
  .domain([0, d3.max(data)])            // 输入域:0 到最大值
  .range([400, 0]);                     // 输出域:像素(注意 SVG 坐标系从上到下)

// ── 4. 设置属性(用函数接收每个数据点)──
bars
  .attr('x', (d, i) => xScale(i))       // d=数据值, i=索引
  .attr('y', d => yScale(d))            // 根据数据计算 y 坐标
  .attr('width', xScale.bandwidth())    // 柱子宽度
  .attr('height', d => 400 - yScale(d)) // 柱子高度
  .attr('fill', '#4e79a7');             // 柱子颜色

// ── 5. 添加坐标轴 ──
const xAxis = d3.axisBottom(xScale);   // 创建 X 轴
svg.append('g')                         // 新增一个分组元素
  .attr('transform', 'translate(0,400)')// 移到底部
  .call(xAxis);                         // 把坐标轴渲染进去

实战案例

// ── 折线图:绘制基因表达量随时间变化 ──
import * as d3 from 'd3';

const data = [
  { time: 0, expression: 1.2 },   // 时间点0,表达量1.2
  { time: 1, expression: 2.8 },
  { time: 2, expression: 4.1 },
  { time: 3, expression: 3.5 },
  { time: 4, expression: 5.0 },
];

const width = 500, height = 300;    // 画布尺寸
const margin = { top: 20, right: 20, bottom: 40, left: 50 }; // 边距

const svg = d3.select('body').append('svg')
  .attr('width', width + margin.left + margin.right)
  .attr('height', height + margin.top + margin.bottom)
  .append('g')
  .attr('transform', `translate(${margin.left},${margin.top})`);  // 留出边距空间

// X 轴比例尺
const x = d3.scaleLinear()
  .domain([0, d3.max(data, d => d.time)])   // 取时间的最大值
  .range([0, width]);

// Y 轴比例尺
const y = d3.scaleLinear()
  .domain([0, d3.max(data, d => d.expression)])  // 取表达量最大值
  .range([height, 0]);

// 折线生成器
const line = d3.line()
  .x(d => x(d.time))          // 每个点的 X 坐标
  .y(d => y(d.expression))    // 每个点的 Y 坐标
  .curve(d3.curveMonotoneX);  // 平滑曲线

// 画折线
svg.append('path')
  .datum(data)                          // 整体数据(不是逐点绑定)
  .attr('fill', 'none')                 // 只画线,不填充
  .attr('stroke', '#e15759')            // 线条颜色
  .attr('stroke-width', 2)              // 线条粗细
  .attr('d', line);                     // 用 line 生成器计算路径

// 添加坐标轴
svg.append('g').attr('transform', `translate(0,${height})`).call(d3.axisBottom(x));
svg.append('g').call(d3.axisLeft(y));  // 左侧 Y 轴

常见报错与解决

报错原因解决方法
Cannot read properties of null选择器没找到元素确认 HTML 中存在对应 id/class
图表显示空白数据绑定后没有 .join().data() 后加 .join('rect')
Y 轴方向反了SVG 坐标系原点在左上角range([height, 0]) 而非 [0, height]
柱子重叠scaleBand 没设 padding.padding(0.1)
路径不显示path 元素有 fill.attr('fill', 'none')

速查表

// 常用比例尺
d3.scaleLinear()      // 线性:连续数值
d3.scaleBand()        // 分组:离散类别
d3.scaleLog()         // 对数:跨越多个数量级
d3.scaleTime()        // 时间:Date 对象

// 常用形状生成器
d3.line()             // 折线
d3.area()             // 面积图
d3.arc()              // 扇形(饼图)
d3.symbol()           // 点形状

// 常用数组工具
d3.max(arr, fn)       // 最大值
d3.min(arr, fn)       // 最小值
d3.extent(arr, fn)    // [最小, 最大]
d3.sum(arr, fn)       // 求和
d3.group(arr, fn)     // 按 key 分组

// 官方文档:https://d3js.org/
// 示例库:https://observablehq.com/@d3