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