D3.js 坐标轴 #
坐标轴是数据可视化的重要组成部分,它们为图表提供参考框架,帮助用户理解数据的范围和比例。
坐标轴基础 #
核心概念 #
text
┌─────────────────────────────────────────────────────────────┐
│ 坐标轴组成 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────┐ │
│ │ │ │
│ │ 刻度线 (Tick Lines) │ │
│ │ │ │ │
│ │ ─────────┼──────────────────────── │ │
│ │ │ │ │
│ │ 刻度标签 (Tick Labels) │ │
│ │ 0 │ │
│ │ │ │
│ └──────────────────────────────────────────┘ │
│ 轴线 (Axis Line) │
│ │
└─────────────────────────────────────────────────────────────┘
坐标轴方向 #
| 方法 | 描述 | 位置 |
|---|---|---|
| axisTop | 顶部轴 | 刻度在轴线下方 |
| axisBottom | 底部轴 | 刻度在轴线上方 |
| axisLeft | 左侧轴 | 刻度在轴线右侧 |
| axisRight | 右侧轴 | 刻度在轴线左侧 |
创建坐标轴 #
基本用法 #
javascript
const xScale = d3.scaleLinear()
.domain([0, 100])
.range([0, 500]);
const xAxis = d3.axisBottom(xScale);
svg.append('g')
.attr('transform', `translate(0, ${height})`)
.call(xAxis);
四个方向 #
javascript
const xScale = d3.scaleLinear()
.domain([0, 100])
.range([0, 500]);
const yScale = d3.scaleLinear()
.domain([0, 100])
.range([height, 0]);
const xAxisTop = d3.axisTop(xScale);
const xAxisBottom = d3.axisBottom(xScale);
const yAxisLeft = d3.axisLeft(yScale);
const yAxisRight = d3.axisRight(yScale);
svg.append('g').call(xAxisTop);
svg.append('g').attr('transform', `translate(0, ${height})`).call(xAxisBottom);
svg.append('g').call(yAxisLeft);
svg.append('g').attr('transform', `translate(${width}, 0)`).call(yAxisRight);
刻度控制 #
ticks 方法 #
设置刻度数量:
javascript
const axis = d3.axisBottom(xScale)
.ticks(5);
const axis = d3.axisBottom(xScale)
.ticks(10);
const axis = d3.axisBottom(xScale)
.ticks(5, '.0f');
const axis = d3.axisBottom(xScale)
.ticks(5, '$.2f');
tickValues 方法 #
指定刻度值:
javascript
const axis = d3.axisBottom(xScale)
.tickValues([0, 25, 50, 75, 100]);
const axis = d3.axisBottom(xScale)
.tickValues(xScale.ticks(5));
tickSize 方法 #
设置刻度线长度:
javascript
const axis = d3.axisBottom(xScale)
.tickSize(10);
const axis = d3.axisBottom(xScale)
.tickSizeInner(6)
.tickSizeOuter(12);
tickPadding 方法 #
设置刻度标签间距:
javascript
const axis = d3.axisBottom(xScale)
.tickPadding(10);
tickFormat 方法 #
设置刻度格式:
javascript
const axis = d3.axisBottom(xScale)
.tickFormat(d3.format('.0f'));
const axis = d3.axisBottom(xScale)
.tickFormat(d => `${d}%`);
const axis = d3.axisBottom(xScale)
.tickFormat(d3.format('$,.0f'));
const axis = d3.axisBottom(xScale)
.tickFormat(d3.format('.2s'));
序数坐标轴 #
使用 scaleBand #
javascript
const xScale = d3.scaleBand()
.domain(['A', 'B', 'C', 'D', 'E'])
.range([0, 500])
.padding(0.2);
const xAxis = d3.axisBottom(xScale);
svg.append('g')
.attr('transform', `translate(0, ${height})`)
.call(xAxis);
自定义刻度 #
javascript
const axis = d3.axisBottom(xScale)
.tickValues(['A', 'C', 'E'])
.tickFormat(d => `Category ${d}`);
时间坐标轴 #
基本用法 #
javascript
const xScale = d3.scaleTime()
.domain([new Date(2020, 0, 1), new Date(2020, 11, 31)])
.range([0, 500]);
const xAxis = d3.axisBottom(xScale);
svg.append('g')
.attr('transform', `translate(0, ${height})`)
.call(xAxis);
时间刻度间隔 #
javascript
const axis = d3.axisBottom(xScale)
.ticks(d3.timeMonth.every(1));
const axis = d3.axisBottom(xScale)
.ticks(d3.timeWeek.every(2));
const axis = d3.axisBottom(xScale)
.ticks(d3.timeDay.every(7));
时间格式化 #
javascript
const axis = d3.axisBottom(xScale)
.tickFormat(d3.timeFormat('%Y-%m'));
const axis = d3.axisBottom(xScale)
.tickFormat(d3.timeFormat('%b %Y'));
const axis = d3.axisBottom(xScale)
.tickFormat(d3.timeFormat('%H:%M'));
多行时间标签 #
javascript
const axis = d3.axisBottom(xScale)
.tickFormat(d => {
const date = new Date(d);
return `${date.getFullYear()}\n${d3.timeFormat('%b')(date)}`;
});
svg.selectAll('.tick text')
.call(wrap, 50);
function wrap(text, width) {
text.each(function() {
const text = d3.select(this);
const lines = text.text().split('\n');
text.text(null);
lines.forEach((line, i) => {
text.append('tspan')
.attr('x', 0)
.attr('dy', i === 0 ? '0em' : '1.2em')
.text(line);
});
});
}
坐标轴样式 #
使用 CSS #
css
.axis path,
.axis line {
fill: none;
stroke: #999;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
fill: #666;
}
javascript
svg.append('g')
.attr('class', 'x axis')
.call(xAxis);
svg.append('g')
.attr('class', 'y axis')
.call(yAxis);
隐藏轴线 #
javascript
const axis = d3.axisBottom(xScale)
.tickSize(0);
svg.select('.domain').remove();
隐藏刻度线 #
javascript
const axis = d3.axisBottom(xScale)
.tickSize(0);
网格线 #
javascript
const axis = d3.axisBottom(xScale)
.tickSize(-height);
const axis = d3.axisLeft(yScale)
.tickSize(-width);
svg.append('g')
.attr('class', 'grid')
.attr('transform', `translate(0, ${height})`)
.call(d3.axisBottom(xScale).tickSize(-height).tickFormat(''));
svg.append('g')
.attr('class', 'grid')
.call(d3.axisLeft(yScale).tickSize(-width).tickFormat(''));
网格线样式 #
css
.grid line {
stroke: #ddd;
stroke-dasharray: 2, 2;
shape-rendering: crispEdges;
}
.grid path {
stroke-width: 0;
}
坐标轴标签 #
添加轴标题 #
javascript
svg.append('text')
.attr('class', 'x-label')
.attr('x', width / 2)
.attr('y', height + 40)
.attr('text-anchor', 'middle')
.text('X Axis Label');
svg.append('text')
.attr('class', 'y-label')
.attr('transform', 'rotate(-90)')
.attr('x', -height / 2)
.attr('y', -40)
.attr('text-anchor', 'middle')
.text('Y Axis Label');
旋转刻度标签 #
javascript
svg.selectAll('.x.axis .tick text')
.attr('transform', 'rotate(-45)')
.attr('text-anchor', 'end')
.attr('dx', '-.8em')
.attr('dy', '.15em');
动态更新坐标轴 #
更新比例尺 #
javascript
function updateAxis(data) {
xScale.domain([0, d3.max(data, d => d.x)]);
yScale.domain([0, d3.max(data, d => d.y)]);
svg.select('.x.axis')
.transition()
.duration(500)
.call(xAxis);
svg.select('.y.axis')
.transition()
.duration(500)
.call(yAxis);
}
平滑过渡 #
javascript
function updateAxis(newDomain) {
xScale.domain(newDomain);
svg.select('.x.axis')
.transition()
.duration(750)
.ease(d3.easeLinear)
.call(xAxis);
}
完整图表示例 #
javascript
const margin = { top: 20, right: 20, bottom: 50, left: 60 };
const width = 600 - margin.left - margin.right;
const height = 400 - margin.top - margin.bottom;
const data = [
{ x: 0, y: 10 },
{ x: 1, y: 20 },
{ x: 2, y: 15 },
{ x: 3, y: 25 },
{ x: 4, y: 18 }
];
const xScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.x)])
.range([0, width]);
const yScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.y)])
.range([height, 0]);
const xAxis = d3.axisBottom(xScale)
.ticks(5)
.tickFormat(d => `X${d}`);
const yAxis = d3.axisLeft(yScale)
.ticks(5)
.tickFormat(d => `${d}%`);
const svg = d3.select('#chart')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom);
const g = svg.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`);
g.append('g')
.attr('class', 'x axis')
.attr('transform', `translate(0, ${height})`)
.call(xAxis);
g.append('g')
.attr('class', 'y axis')
.call(yAxis);
g.append('text')
.attr('class', 'x-label')
.attr('x', width / 2)
.attr('y', height + 40)
.attr('text-anchor', 'middle')
.text('X Axis');
g.append('text')
.attr('class', 'y-label')
.attr('transform', 'rotate(-90)')
.attr('x', -height / 2)
.attr('y', -40)
.attr('text-anchor', 'middle')
.text('Y Axis');
g.selectAll('.point')
.data(data)
.enter()
.append('circle')
.attr('class', 'point')
.attr('cx', d => xScale(d.x))
.attr('cy', d => yScale(d.y))
.attr('r', 5)
.attr('fill', 'steelblue');
坐标轴方法速查表 #
| 方法 | 描述 | 示例 |
|---|---|---|
| ticks | 设置刻度数量 | .ticks(5) |
| tickValues | 指定刻度值 | .tickValues([0, 50, 100]) |
| tickSize | 设置刻度线长度 | .tickSize(10) |
| tickSizeInner | 设置内部刻度线 | .tickSizeInner(6) |
| tickSizeOuter | 设置外部刻度线 | .tickSizeOuter(12) |
| tickPadding | 设置标签间距 | .tickPadding(10) |
| tickFormat | 设置刻度格式 | .tickFormat(d3.format('.0f')) |
下一步 #
现在你已经掌握了坐标轴的创建和自定义,接下来学习 过渡动画,了解如何为图表添加动画效果!
最后更新:2026-03-28