D3.js SVG 基础 #

SVG(Scalable Vector Graphics,可缩放矢量图形)是 D3.js 数据可视化的核心载体。理解 SVG 是掌握 D3.js 的基础。

SVG 简介 #

什么是 SVG? #

SVG 是一种基于 XML 的矢量图形格式,具有以下特点:

text
┌─────────────────────────────────────────────────────────────┐
│                    SVG 特点                                  │
├─────────────────────────────────────────────────────────────┤
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │   矢量图形   │  │   可缩放     │  │   可交互    │         │
│  │  无限放大    │  │  不失真      │  │  支持事件   │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │   DOM 元素   │  │   样式控制   │  │   动画支持  │         │
│  │  可操作      │  │  CSS/属性   │  │  SMIL/JS    │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
└─────────────────────────────────────────────────────────────┘

SVG vs Canvas #

特性 SVG Canvas
渲染方式 矢量 位图
缩放 无损 有损
事件支持 原生 需要计算
性能 元素多时较慢 大量元素性能好
适用场景 图表、图标 游戏、图像处理

创建 SVG #

基本结构 #

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

设置 viewBox #

javascript
const svg = d3.select('#chart')
  .append('svg')
  .attr('viewBox', '0 0 500 300')
  .attr('preserveAspectRatio', 'xMidYMid meet');

响应式 SVG #

javascript
const svg = d3.select('#chart')
  .append('svg')
  .attr('width', '100%')
  .attr('height', '100%')
  .attr('viewBox', '0 0 500 300');

SVG 坐标系统 #

坐标原点 #

text
(0, 0) ────────────────► x
   │
   │
   │
   │
   ▼
   y

SVG 坐标原点在左上角,x 轴向右增长,y 轴向下增长。

transform 属性 #

javascript
svg.append('g')
  .attr('transform', 'translate(50, 50)');

svg.append('g')
  .attr('transform', 'translate(50, 50) scale(2)');

svg.append('g')
  .attr('transform', 'translate(50, 50) rotate(45)');

svg.append('g')
  .attr('transform', 'translate(50, 50) rotate(45, 100, 100)');

基本图形元素 #

矩形 rect #

javascript
svg.append('rect')
  .attr('x', 50)
  .attr('y', 50)
  .attr('width', 100)
  .attr('height', 80)
  .attr('fill', 'steelblue')
  .attr('stroke', '#333')
  .attr('stroke-width', 2)
  .attr('rx', 5)
  .attr('ry', 5);

圆形 circle #

javascript
svg.append('circle')
  .attr('cx', 100)
  .attr('cy', 100)
  .attr('r', 50)
  .attr('fill', 'orange')
  .attr('stroke', '#333')
  .attr('stroke-width', 2);

椭圆 ellipse #

javascript
svg.append('ellipse')
  .attr('cx', 100)
  .attr('cy', 100)
  .attr('rx', 80)
  .attr('ry', 50)
  .attr('fill', 'lightgreen');

线 line #

javascript
svg.append('line')
  .attr('x1', 50)
  .attr('y1', 50)
  .attr('x2', 200)
  .attr('y2', 150)
  .attr('stroke', 'black')
  .attr('stroke-width', 2)
  .attr('stroke-dasharray', '5,5');

折线 polyline #

javascript
svg.append('polyline')
  .attr('points', '50,50 100,100 150,50 200,100')
  .attr('fill', 'none')
  .attr('stroke', 'blue')
  .attr('stroke-width', 2);

多边形 polygon #

javascript
svg.append('polygon')
  .attr('points', '100,10 40,198 190,78 10,78 160,198')
  .attr('fill', 'gold')
  .attr('stroke', 'orange')
  .attr('stroke-width', 2);

路径 path #

javascript
svg.append('path')
  .attr('d', 'M 10 10 L 100 10 L 100 100 L 10 100 Z')
  .attr('fill', 'lightblue')
  .attr('stroke', 'blue');

svg.append('path')
  .attr('d', 'M 10 80 Q 95 10 180 80 T 350 80')
  .attr('fill', 'none')
  .attr('stroke', 'red')
  .attr('stroke-width', 2);

文本 text #

javascript
svg.append('text')
  .attr('x', 100)
  .attr('y', 100)
  .attr('text-anchor', 'middle')
  .attr('dominant-baseline', 'middle')
  .attr('font-size', '20px')
  .attr('fill', 'black')
  .text('Hello D3.js');

svg.append('text')
  .attr('x', 100)
  .attr('y', 100)
  .attr('transform', 'rotate(-45, 100, 100)')
  .text('Rotated Text');

分组元素 g #

创建分组 #

javascript
const g = svg.append('g')
  .attr('class', 'chart-group')
  .attr('transform', 'translate(50, 50)');

g.append('circle')
  .attr('cx', 50)
  .attr('cy', 50)
  .attr('r', 30);

g.append('text')
  .attr('x', 50)
  .attr('y', 50)
  .text('Group');

分组的作用 #

javascript
const margin = { top: 20, right: 20, bottom: 30, left: 40 };

const g = svg.append('g')
  .attr('transform', `translate(${margin.left}, ${margin.top})`);

const xGroup = g.append('g')
  .attr('class', 'x-axis')
  .attr('transform', `translate(0, ${height})`);

const yGroup = g.append('g')
  .attr('class', 'y-axis');

样式属性 #

填充 fill #

javascript
svg.append('rect')
  .attr('fill', 'steelblue')
  .attr('fill', '#3498db')
  .attr('fill', 'rgb(52, 152, 219)')
  .attr('fill', 'rgba(52, 152, 219, 0.5)')
  .attr('fill', 'url(#gradient)');

描边 stroke #

javascript
svg.append('rect')
  .attr('stroke', '#333')
  .attr('stroke-width', 2)
  .attr('stroke-dasharray', '5,5')
  .attr('stroke-linecap', 'round')
  .attr('stroke-linejoin', 'round')
  .attr('stroke-opacity', 0.5);

透明度 opacity #

javascript
svg.append('circle')
  .attr('opacity', 0.5)
  .attr('fill-opacity', 0.7)
  .attr('stroke-opacity', 0.3);

渐变和图案 #

线性渐变 #

javascript
const defs = svg.append('defs');

const linearGradient = defs.append('linearGradient')
  .attr('id', 'myGradient')
  .attr('x1', '0%')
  .attr('y1', '0%')
  .attr('x2', '100%')
  .attr('y2', '0%');

linearGradient.append('stop')
  .attr('offset', '0%')
  .attr('stop-color', 'steelblue');

linearGradient.append('stop')
  .attr('offset', '100%')
  .attr('stop-color', 'orange');

svg.append('rect')
  .attr('fill', 'url(#myGradient)')
  .attr('width', 200)
  .attr('height', 100);

径向渐变 #

javascript
const radialGradient = defs.append('radialGradient')
  .attr('id', 'radialGradient')
  .attr('cx', '50%')
  .attr('cy', '50%')
  .attr('r', '50%');

radialGradient.append('stop')
  .attr('offset', '0%')
  .attr('stop-color', 'white');

radialGradient.append('stop')
  .attr('offset', '100%')
  .attr('stop-color', 'steelblue');

图案 #

javascript
const pattern = defs.append('pattern')
  .attr('id', 'pattern')
  .attr('width', 10)
  .attr('height', 10)
  .attr('patternUnits', 'userSpaceOnUse');

pattern.append('rect')
  .attr('width', 10)
  .attr('height', 10)
  .attr('fill', 'white');

pattern.append('circle')
  .attr('cx', 5)
  .attr('cy', 5)
  .attr('r', 2)
  .attr('fill', 'steelblue');

svg.append('rect')
  .attr('fill', 'url(#pattern)')
  .attr('width', 200)
  .attr('height', 100);

路径命令 #

M - 移动到 #

javascript
svg.append('path')
  .attr('d', 'M 10 10');

L - 直线到 #

javascript
svg.append('path')
  .attr('d', 'M 10 10 L 100 10 L 100 100');

H/V - 水平/垂直线 #

javascript
svg.append('path')
  .attr('d', 'M 10 10 H 100 V 100 H 10 Z');

C - 三次贝塞尔曲线 #

javascript
svg.append('path')
  .attr('d', 'M 10 10 C 20 20, 40 20, 50 10')
  .attr('fill', 'none')
  .attr('stroke', 'black');

S - 平滑三次贝塞尔 #

javascript
svg.append('path')
  .attr('d', 'M 10 10 C 20 20, 40 20, 50 10 S 80 0, 90 10')
  .attr('fill', 'none')
  .attr('stroke', 'black');

Q - 二次贝塞尔曲线 #

javascript
svg.append('path')
  .attr('d', 'M 10 10 Q 50 0, 90 10')
  .attr('fill', 'none')
  .attr('stroke', 'black');

T - 平滑二次贝塞尔 #

javascript
svg.append('path')
  .attr('d', 'M 10 10 Q 50 0, 90 10 T 170 10')
  .attr('fill', 'none')
  .attr('stroke', 'black');

A - 弧线 #

javascript
svg.append('path')
  .attr('d', 'M 10 10 A 30 30 0 0 1 90 10')
  .attr('fill', 'none')
  .attr('stroke', 'black');

Z - 闭合路径 #

javascript
svg.append('path')
  .attr('d', 'M 10 10 L 100 10 L 100 100 Z')
  .attr('fill', 'lightblue');

D3.js 路径生成器 #

d3.path() #

javascript
const path = d3.path();
path.moveTo(10, 10);
path.lineTo(100, 10);
path.lineTo(100, 100);
path.closePath();

svg.append('path')
  .attr('d', path.toString());

d3.line() #

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

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');

d3.area() #

javascript
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');

d3.arc() #

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

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

SVG 与 CSS #

使用 CSS 样式 #

css
.bar {
  fill: steelblue;
  stroke: #333;
  stroke-width: 1;
}

.bar:hover {
  fill: orange;
}
javascript
svg.selectAll('rect')
  .data(data)
  .enter()
  .append('rect')
  .attr('class', 'bar');

内联样式 #

javascript
svg.append('rect')
  .style('fill', 'steelblue')
  .style('stroke', '#333')
  .style('stroke-width', '1px');

SVG 事件 #

添加事件监听 #

javascript
svg.append('circle')
  .attr('cx', 100)
  .attr('cy', 100)
  .attr('r', 50)
  .attr('fill', 'steelblue')
  .on('click', function(event, d) {
    d3.select(this).attr('fill', 'orange');
  })
  .on('mouseover', function() {
    d3.select(this).attr('r', 60);
  })
  .on('mouseout', function() {
    d3.select(this).attr('r', 50);
  });

SVG 最佳实践 #

1. 使用分组组织元素 #

javascript
const chart = svg.append('g')
  .attr('class', 'chart');

const bars = chart.append('g')
  .attr('class', 'bars');

const axes = chart.append('g')
  .attr('class', 'axes');

2. 使用 defs 定义可重用元素 #

javascript
const defs = svg.append('defs');

defs.append('circle')
  .attr('id', 'myCircle')
  .attr('r', 10);

svg.append('use')
  .attr('href', '#myCircle')
  .attr('x', 50)
  .attr('y', 50);

3. 设置合适的 viewBox #

javascript
const svg = d3.select('#chart')
  .append('svg')
  .attr('viewBox', `0 0 ${width} ${height}`)
  .attr('preserveAspectRatio', 'xMidYMid meet');

4. 使用 transform 简化定位 #

javascript
const margin = { top: 20, right: 20, bottom: 30, left: 40 };

const g = svg.append('g')
  .attr('transform', `translate(${margin.left}, ${margin.top})`);

SVG 元素速查表 #

元素 描述 主要属性
svg SVG 容器 width, height, viewBox
g 分组 transform
rect 矩形 x, y, width, height, rx, ry
circle 圆形 cx, cy, r
ellipse 椭圆 cx, cy, rx, ry
line 线 x1, y1, x2, y2
polyline 折线 points
polygon 多边形 points
path 路径 d
text 文本 x, y, text-anchor
defs 定义 -
use 引用 href

下一步 #

现在你已经掌握了 SVG 基础,接下来学习 形状生成器,了解如何使用 D3.js 创建更复杂的图形!

最后更新:2026-03-28