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.link #
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