D3.js 形状生成器 #

形状生成器是 D3.js 提供的工具函数,用于生成各种图形的路径数据。它们将数据转换为 SVG path 元素的 d 属性值。

形状生成器概述 #

核心概念 #

text
┌─────────────────────────────────────────────────────────────┐
│                    形状生成器工作流程                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   数据 ──► 形状生成器 ──► 路径字符串 ──► path 元素          │
│                                                             │
│   [x,y]    d3.line()    "M 0,10 L 1,20..."   <path d="..."> │
│                                                             │
└─────────────────────────────────────────────────────────────┘

形状生成器类型 #

生成器 描述 用途
d3.line 线生成器 折线图、曲线图
d3.area 区域生成器 面积图
d3.arc 弧生成器 饼图、环形图
d3.pie 饼图生成器 饼图数据转换
d3.symbol 符号生成器 散点图符号
d3.link 连线生成器 关系图连线

线生成器 d3.line #

基本用法 #

javascript
const data = [
  { x: 0, y: 10 },
  { x: 1, y: 20 },
  { x: 2, y: 15 },
  { x: 3, y: 25 }
];

const line = d3.line()
  .x(d => d.x * 50)
  .y(d => d.y * 5);

svg.append('path')
  .datum(data)
  .attr('d', line)
  .attr('fill', 'none')
  .attr('stroke', 'steelblue')
  .attr('stroke-width', 2);

设置访问器 #

javascript
const line = d3.line()
  .x(d => xScale(d.x))
  .y(d => yScale(d.y));

const line = d3.line()
  .x((d, i) => i * 50)
  .y(d => d.value);

const line = d3.line()
  .defined(d => d.value !== null);

曲线插值 #

javascript
const line = d3.line()
  .x(d => d.x)
  .y(d => d.y)
  .curve(d3.curveLinear);

const line = d3.line()
  .curve(d3.curveBasis);

const line = d3.line()
  .curve(d3.curveCardinal);

const line = d3.line()
  .curve(d3.curveMonotoneX);

const line = d3.line()
  .curve(d3.curveCatmullRom);

const line = d3.line()
  .curve(d3.curveStep);

const line = d3.line()
  .curve(d3.curveStepBefore);

const line = d3.line()
  .curve(d3.curveStepAfter);

曲线类型对比 #

曲线类型 描述 特点
curveLinear 直线连接 默认,折线
curveBasis B样条曲线 平滑
curveCardinal Cardinal样条 平滑,过点
curveMonotoneX 单调曲线 保持单调性
curveCatmullRom Catmull-Rom 平滑,过点
curveStep 阶梯曲线 水平垂直
curveStepBefore 前阶梯 先垂直
curveStepAfter 后阶梯 先水平

处理缺失数据 #

javascript
const data = [
  { x: 0, y: 10 },
  { x: 1, y: null },
  { x: 2, y: 15 },
  { x: 3, y: null },
  { x: 4, y: 25 }
];

const line = d3.line()
  .x(d => d.x * 50)
  .y(d => d.y * 5)
  .defined(d => d.y !== null);

区域生成器 d3.area #

基本用法 #

javascript
const data = [
  { x: 0, y: 10 },
  { x: 1, y: 20 },
  { x: 2, y: 15 },
  { x: 3, y: 25 }
];

const area = d3.area()
  .x(d => d.x * 50)
  .y0(height)
  .y1(d => height - d.y * 5);

svg.append('path')
  .datum(data)
  .attr('d', area)
  .attr('fill', 'steelblue');

设置访问器 #

javascript
const area = d3.area()
  .x(d => xScale(d.x))
  .y0(d => yScale(d.y0))
  .y1(d => yScale(d.y1));

const area = d3.area()
  .x((d, i) => i * 50)
  .y0(0)
  .y1(d => d.value);

const area = d3.area()
  .x(d => xScale(d.date))
  .y0(height - margin.bottom)
  .y1(d => yScale(d.value));

曲线插值 #

javascript
const area = d3.area()
  .x(d => d.x)
  .y0(height)
  .y1(d => d.y)
  .curve(d3.curveMonotoneX);

const area = d3.area()
  .curve(d3.curveBasis);

获取线条路径 #

javascript
const area = d3.area()
  .x(d => d.x)
  .y0(height)
  .y1(d => d.y);

area.lineY0();
area.lineY1();

堆叠面积图 #

javascript
const area = d3.area()
  .x(d => xScale(d.data.date))
  .y0(d => yScale(d[0]))
  .y1(d => yScale(d[1]))
  .curve(d3.curveMonotoneX);

弧生成器 d3.arc #

基本用法 #

javascript
const arc = d3.arc()
  .innerRadius(50)
  .outerRadius(100)
  .startAngle(0)
  .endAngle(Math.PI / 2);

svg.append('path')
  .attr('d', arc)
  .attr('transform', 'translate(150, 150)')
  .attr('fill', 'steelblue');

饼图弧 #

javascript
const arc = d3.arc()
  .innerRadius(0)
  .outerRadius(100)
  .padAngle(0.02)
  .cornerRadius(5);

const data = [1, 1, 2, 3, 5, 8];
const pie = d3.pie()(data);

svg.selectAll('path')
  .data(pie)
  .enter()
  .append('path')
  .attr('d', arc)
  .attr('transform', 'translate(150, 150)')
  .attr('fill', (d, i) => d3.schemeCategory10[i]);

环形图弧 #

javascript
const arc = d3.arc()
  .innerRadius(60)
  .outerRadius(100);

圆角弧 #

javascript
const arc = d3.arc()
  .innerRadius(50)
  .outerRadius(100)
  .cornerRadius(10);

间距弧 #

javascript
const arc = d3.arc()
  .innerRadius(50)
  .outerRadius(100)
  .padAngle(0.05);

弧形质心 #

javascript
const arc = d3.arc()
  .innerRadius(50)
  .outerRadius(100);

svg.selectAll('path')
  .data(pie)
  .enter()
  .append('path')
  .attr('d', arc)
  .each(function(d) {
    const centroid = arc.centroid(d);
    d3.select(this.parentNode)
      .append('text')
      .attr('x', centroid[0])
      .attr('y', centroid[1])
      .text(d.data);
  });

饼图生成器 d3.pie #

基本用法 #

javascript
const data = [10, 20, 30, 40, 50];
const pie = d3.pie()(data);

pie.forEach(d => {
  console.log(d.value);
  console.log(d.startAngle);
  console.log(d.endAngle);
  console.log(d.index);
});

设置值访问器 #

javascript
const data = [
  { name: 'A', value: 10 },
  { name: 'B', value: 20 },
  { name: 'C', value: 30 }
];

const pie = d3.pie()
  .value(d => d.value)
  .sort((a, b) => a.value - b.value);

const pieData = pie(data);

排序 #

javascript
const pie = d3.pie()
  .sort(null);

const pie = d3.pie()
  .sort((a, b) => a.value - b.value);

const pie = d3.pie()
  .sortValues((a, b) => a - b);

起始和结束角度 #

javascript
const pie = d3.pie()
  .startAngle(0)
  .endAngle(Math.PI);

const pie = d3.pie()
  .startAngle(-Math.PI / 2)
  .endAngle(Math.PI / 2);

间距 #

javascript
const pie = d3.pie()
  .padAngle(0.02);

完整饼图示例 #

javascript
const data = [10, 20, 30, 40, 50];
const width = 300;
const height = 300;
const radius = Math.min(width, height) / 2;

const pie = d3.pie()
  .value(d => d)
  .sort(null);

const arc = d3.arc()
  .innerRadius(0)
  .outerRadius(radius);

const arcLabel = d3.arc()
  .innerRadius(radius * 0.6)
  .outerRadius(radius * 0.6);

const svg = d3.select('#chart')
  .append('svg')
  .attr('width', width)
  .attr('height', height);

const g = svg.append('g')
  .attr('transform', `translate(${width/2}, ${height/2})`);

const arcs = g.selectAll('.arc')
  .data(pie(data))
  .enter()
  .append('g')
  .attr('class', 'arc');

arcs.append('path')
  .attr('d', arc)
  .attr('fill', (d, i) => d3.schemeCategory10[i]);

arcs.append('text')
  .attr('transform', d => `translate(${arcLabel.centroid(d)})`)
  .attr('text-anchor', 'middle')
  .text(d => d.data);

符号生成器 d3.symbol #

基本用法 #

javascript
const symbol = d3.symbol()
  .type(d3.symbolCircle)
  .size(100);

svg.append('path')
  .attr('d', symbol)
  .attr('transform', 'translate(100, 100)')
  .attr('fill', 'steelblue');

符号类型 #

javascript
d3.symbolCircle
d3.symbolCross
d3.symbolDiamond
d3.symbolSquare
d3.symbolStar
d3.symbolTriangle
d3.symbolWye
d3.symbolAsterisk
d3.symbolDiamond2
d3.symbolPlus
d3.symbolSquare2
d3.symbolTriangle2

动态符号 #

javascript
const symbols = d3.symbols;

svg.selectAll('path')
  .data(symbols)
  .enter()
  .append('path')
  .attr('d', d3.symbol().type(d => d).size(200))
  .attr('transform', (d, i) => `translate(${i * 50 + 50}, 100)`)
  .attr('fill', 'steelblue');

散点图符号 #

javascript
const data = [
  { x: 10, y: 20, type: 'A' },
  { x: 30, y: 40, type: 'B' },
  { x: 50, y: 60, type: 'A' }
];

const symbolScale = d3.scaleOrdinal()
  .domain(['A', 'B'])
  .range([d3.symbolCircle, d3.symbolSquare]);

svg.selectAll('path')
  .data(data)
  .enter()
  .append('path')
  .attr('d', d => d3.symbol().type(symbolScale(d.type)).size(100)())
  .attr('transform', d => `translate(${d.x}, ${d.y})`)
  .attr('fill', 'steelblue');

d3.linkHorizontal #

javascript
const data = {
  source: [0, 0],
  target: [200, 100]
};

const link = d3.linkHorizontal()
  .source(d => d.source)
  .target(d => d.target);

svg.append('path')
  .datum(data)
  .attr('d', link)
  .attr('fill', 'none')
  .attr('stroke', 'steelblue');

d3.linkVertical #

javascript
const link = d3.linkVertical()
  .source(d => d.source)
  .target(d => d.target);

自定义连线 #

javascript
const link = d3.link(d3.curveBasis)
  .x(d => d.x)
  .y(d => d.y);

树图连线 #

javascript
const link = d3.linkHorizontal()
  .x(d => d.y)
  .y(d => d.x);

svg.selectAll('path')
  .data(root.links())
  .enter()
  .append('path')
  .attr('d', link)
  .attr('fill', 'none')
  .attr('stroke', '#999');

堆叠生成器 d3.stack #

基本用法 #

javascript
const data = [
  { month: 'Jan', apples: 10, oranges: 5, pears: 3 },
  { month: 'Feb', apples: 15, oranges: 8, pears: 5 },
  { month: 'Mar', apples: 20, oranges: 12, pears: 7 }
];

const keys = ['apples', 'oranges', 'pears'];

const stack = d3.stack()
  .keys(keys)
  .order(d3.stackOrderNone)
  .offset(d3.stackOffsetNone);

const series = stack(data);

series.forEach(s => {
  console.log(s.key);
  s.forEach(d => {
    console.log(d[0], d[1]);
  });
});

堆叠顺序 #

javascript
d3.stackOrderNone
d3.stackOrderAscending
d3.stackOrderDescending
d3.stackOrderInsideOut
d3.stackOrderReverse

堆叠偏移 #

javascript
d3.stackOffsetNone
d3.stackOffsetExpand
d3.stackOffsetDiverging
d3.stackOffsetSilhouette
d3.stackOffsetWiggle

堆叠面积图 #

javascript
const stack = d3.stack()
  .keys(['apples', 'oranges', 'pears']);

const area = d3.area()
  .x((d, i) => xScale(i))
  .y0(d => yScale(d[0]))
  .y1(d => yScale(d[1]));

svg.selectAll('path')
  .data(stack(data))
  .enter()
  .append('path')
  .attr('d', area)
  .attr('fill', (d, i) => d3.schemeCategory10[i]);

形状生成器速查表 #

生成器 方法 描述
line .x() 设置 x 访问器
line .y() 设置 y 访问器
line .curve() 设置曲线类型
line .defined() 设置有效数据判断
area .y0() 设置基线 y 值
area .y1() 设置顶部 y 值
arc .innerRadius() 设置内半径
arc .outerRadius() 设置外半径
arc .startAngle() 设置起始角度
arc .endAngle() 设置结束角度
pie .value() 设置值访问器
pie .sort() 设置排序函数
symbol .type() 设置符号类型
symbol .size() 设置符号大小

下一步 #

现在你已经掌握了形状生成器,接下来学习 坐标轴,了解如何为图表添加坐标轴!

最后更新:2026-03-28