{T}

比例尺与坐标轴

比例尺(Scale)是 D3 中将**数据域(domain)映射到视觉范围(range)**的核心工具。坐标轴(Axis)则是比例尺的可视化表达。

1. 比例尺体系

图表渲染中…

2. 连续型比例尺

2.1 scaleLinear(线性)

最常用的比例尺,数据与视觉呈线性关系。

javascript
import { scaleLinear } from 'd3'

const yScale = scaleLinear()
  .domain([0, 100])    // 数据范围
  .range([400, 0])     // 像素范围(SVG y 轴向下,所以反转)

yScale(0)    // → 400(底部)
yScale(50)   // → 200(中间)
yScale(100)  // → 0(顶部)

// 反向映射:像素 → 数据
yScale.invert(200)  // → 50

常用配置:

javascript
const scale = scaleLinear()
  .domain([0, 100])
  .range([0, 600])
  .clamp(true)       // 超出 domain 时截断到 range 边界
  .nice()            // 将 domain 扩展到"好看"的整数边界
  .interpolate(d3.interpolateRound)  // 输出整数像素

2.2 scaleTime(时间)

javascript
import { scaleTime, timeMonth, timeFormat } from 'd3'

const xScale = scaleTime()
  .domain([new Date(2024, 0, 1), new Date(2024, 11, 31)])
  .range([0, 800])

xScale(new Date(2024, 5, 15))  // → 约 397(年中)

// 生成刻度(每月一个)
xScale.ticks(timeMonth)  // [Jan, Feb, Mar, ...]

2.3 scaleLog(对数)

适用于跨数量级的数据(如人口、收入)。

javascript
import { scaleLog } from 'd3'

const scale = scaleLog()
  .domain([1, 10000])
  .range([0, 400])

scale(1)     // → 0
scale(10)    // → 100
scale(100)   // → 200
scale(1000)  // → 300
scale(10000) // → 400
domain 不能包含 0

对数比例尺的 domain 不能包含 0(log(0) = -∞)。如果数据有 0 值,使用 scaleSymlog()

3. 离散型比例尺

3.1 scaleBand(带状)

柱状图的标准选择,将离散类目映射为等宽带状区域。

javascript
import { scaleBand } from 'd3'

const xScale = scaleBand()
  .domain(['React', 'Vue', 'Angular', 'Svelte'])
  .range([0, 600])
  .padding(0.2)        // 带宽的 20% 作为间距
  // .paddingInner(0.2)  // 仅内部间距
  // .paddingOuter(0.1)  // 仅外部间距

xScale('Vue')         // → 150(该类目起始 x)
xScale.bandwidth()    // → 120(每个柱子的宽度)
xScale.step()         // → 150(带宽 + 间距)
图表渲染中…

3.2 scalePoint(点)

折线图/散点图的 x 轴,将类目映射为点位置。

javascript
import { scalePoint } from 'd3'

const xScale = scalePoint()
  .domain(['Mon', 'Tue', 'Wed', 'Thu', 'Fri'])
  .range([0, 500])
  .padding(0.5)

xScale('Wed')  // → 250(中间点)
xScale.step()  // → 100(点间距)

3.3 scaleOrdinal(序数)

将离散输入映射到离散输出(常用于颜色)。

javascript
import { scaleOrdinal, schemeCategory10 } from 'd3'

const colorScale = scaleOrdinal()
  .domain(['A', 'B', 'C', 'D'])
  .range(['#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4'])

colorScale('A')  // → '#ff6b6b'
colorScale('B')  // → '#4ecdc4'

// 使用内置配色方案
const color10 = scaleOrdinal(schemeCategory10)

4. 序列型比例尺(颜色映射)

4.1 scaleSequential

将连续数据映射到颜色渐变。

javascript
import { scaleSequential, interpolateViridis } from 'd3'

const colorScale = scaleSequential()
  .domain([0, 100])
  .interpolator(interpolateViridis)

colorScale(0)    // → 深紫色
colorScale(50)   // → 青绿色
colorScale(100)  // → 黄色

4.2 scaleQuantize(量化)

将连续域切分为有限个离散输出。

javascript
import { scaleQuantize } from 'd3'

const scale = scaleQuantize()
  .domain([0, 100])
  .range(['#fee', '#fcc', '#f99', '#f66', '#f33'])

scale(10)  // → '#fee'
scale(90)  // → '#f33'

5. 坐标轴

5.1 创建坐标轴

javascript
import { axisBottom, axisLeft, axisRight, axisTop } from 'd3'

// 底部 x 轴
const xAxis = axisBottom(xScale)
  .ticks(10)                    // 刻度数量(建议值)
  .tickFormat(d3.format('.0f')) // 刻度格式化
  .tickSize(6)                  // 刻度线长度
  .tickPadding(8)               // 文字与刻度线间距

// 左侧 y 轴
const yAxis = axisLeft(yScale)
  .ticks(5)
  .tickFormat(d => `${d}%`)

// 渲染到 SVG
svg.append('g')
  .attr('class', 'x-axis')
  .attr('transform', `translate(0,${height})`)
  .call(xAxis)

svg.append('g')
  .attr('class', 'y-axis')
  .call(yAxis)

5.2 时间轴格式化

javascript
import { timeFormat, timeMonth } from 'd3'

const xAxis = axisBottom(xTimeScale)
  .ticks(timeMonth.every(2))  // 每 2 个月一个刻度
  .tickFormat(timeFormat('%Y-%m'))  // 格式:2024-06

5.3 自定义坐标轴样式

javascript
// 移除轴线
svg.select('.x-axis .domain').remove()

// 虚线网格线
svg.select('.y-axis')
  .call(axisLeft(yScale)
    .tickSize(-width)  // 负值 = 横跨整个图表
    .tickFormat('')    // 隐藏文字
  )
  .selectAll('line')
  .attr('stroke', '#e0e0e0')
  .attr('stroke-dasharray', '3,3')

// 隐藏刻度线
svg.select('.y-axis .domain').remove()

5.4 坐标轴过渡动画

javascript
// 数据更新时平滑过渡坐标轴
function updateChart(newData) {
  yScale.domain([0, d3.max(newData, d => d.value)]).nice()

  svg.select('.y-axis')
    .transition()
    .duration(750)
    .call(axisLeft(yScale))
}

6. 比例尺选型指南

图表类型x 轴比例尺y 轴比例尺
柱状图scaleBandscaleLinear
折线图scaleTime / scalePointscaleLinear
散点图scaleLinearscaleLinear
面积图scaleTimescaleLinear
饼图/环形图不需要(用 arc 生成器)不需要
热力图scaleBandscaleBand
气泡图scaleLinearscaleLinear
雷达图scaleLinear(径向)scalePoint(角度)

7. 最佳实践

始终调用 .nice()
javascript
yScale.domain([0, maxValue]).nice()
// 将 [0, 97] 扩展为 [0, 100],刻度更美观
颜色比例尺使用内置方案

D3 提供 schemeCategory10interpolateViridisinterpolatePlasma 等经过色彩学验证的方案,避免自行配色。

注意 range 方向

SVG 坐标系 y 轴向下,因此 y 比例尺的 range 通常是 [height, 0](反转),使数据越大位置越高。