注意点

  • 左上角为坐标原点 (0,0)
  • 所有图形渲染出来之后默认在原点,需要手动调整位置,可选择更改 xytransform 等属性(此操作开始有点晕,熟悉之后就好了)

API

csvParse()

解析 csv 文件,返回格式化后的对象。示例如下

const data = d3.csvParse(csv, (item) => ({
  date: item.date,
}));

utcParse()

返回一个时间格式化函数。示例如下

const parseDate = d3.utcParse('%Y-%m-%d');

extent()

获取数据集中,指定字段的范围区间,返回一个数组,分别为最小值和最大值。示例如下

const dateRange = d3.extent(data, (d) => d.date);
d3.extent(data, (d) => d.date);

append()

追加元素,和浏览器的 append 方法类似。示例如下

// 直接追加元素
d3.select('.div').append('span');
// 追加独立创建的元素
const span = d3.create('span');
d3.select('.div').append(() => span.node());

attr()

设置属性,接受两个参数

  • 第一个参数:要设置的属性
  • 第二个参数:属性值。可接受一个函数,该函数的返回值将作为值

enter()

返回有数据没有 DOM 的集合,通常与 append() 一起出现

exit()

返回有 DOM 没有数据的集合,通常与 remove() 一起出现

join()

根据需要进行追加、删除、重新排列元素,以匹配先前通过选择绑定的数据,返回合并后的 enterupdate 集合

也就是说 join 是一个简化操作:将 data() 返回的 updateenter 进行 merge,得到最终要渲染的数据

join() 的参数为

  • enter:字符串或回调函数
  • update:回调函数
  • exit:回调函数 分别用于处理数据进入、更新、删除三种状态时的操作,配合 transition() 即可实现动画效果。示例如下
const t = d3.transition().duration(750);
d3.selectAll('text')
  .data(data, (d) => d)
  .join(
    (enter) =>
      enter
        .append('text')
        .attr('fill', 'green')
        .attr('y', -10)
        .transition(t)
        .attr('y', 0),
    (update) =>
      update
        .attr('fill', '#555')
        .transition(t)
        .attr('x', (d, i) => i * 26),
    (exit) => exit.attr('fill', 'red').transition(t).attr('y', 30).remove()
  );

data()element()update()enter()exit() 的关系

stack()

堆叠生成器。将原始数据转换为便于生成这类型图表的数据,一般将堆叠生成器与面积生成器配合使用,也可生成条形图

相关排序函数

  • d3.stackOrderAppearance:在所有数据点中,具有最大值的系列在底部
  • d3.stackOrderAscending:系列所有值之和最小的在底部
  • d3.stackOrderDescending:系列所有值之和最大的在底部
  • d3.stackOrderInsideOut:在所有数据点中,具有最大值的系列在中间(一般用于河流图中)
  • d3.stackOrderNone:默认值,堆叠次序按照系列名称数组 keys 的顺序
  • d3.stackOrderReverse:堆叠次序和系列名称数组 keys 次序相反

相关基线函数

stack.offset(offsetFunc):设置系列堆叠基线函数。默认使用 stack.offset(d3.stackOffsetNone) 即以零为基线

  • d3.stackOffsetExpand:对数据进行标准化,基线是零,上边界线是 1
  • d3.stackOffsetDiverging:允许正值和负值分别进行堆叠,正值会在零之上进行堆叠,负值会在零之下堆叠
  • d3.stackOffsetNone:默认值,以零为基线
  • d3.stackOffsetSilhouette:将基线向下移动,使河流图的中心始终为零
  • d3.stackOffsetWiggle:移动基线,以最大程度地减少各系列在横轴方向上的「摆动」,一般用在河流图中,与 d3.stackOrderInsideOut 排序函数配合使用。

比例尺

d3 的比例尺有很多,常用的有 scaleLinear()scaleBand() 两种,因为直方图的 x 轴和 y 轴就要用到

其返回值是一个函数,使用示例如下

const x = d3
  .scaleLinear()
  .domain([0, d3.max(fruits, (d) => d.count)])
  .range([margin.left, width - margin.right])
  .interpolate(d3.interpolateRound);

const y = d3
  .scaleBand()
  .domain(fruits.map((d) => d.name))
  .range([margin.top, height - margin.bottom])
  .padding(0.1)
  .round(true);
  • widthmargin 是一个手动设置的变量,表示图表的总宽度和内边距
  • domain() :数据范围
  • range():显示范围
  • interpolate:差值器,用于插补任意值,如数字、字符串、颜色、数组、对象等。大白话就是:d3 根据初始提供的数据,自动填充中间的值。示例如下
    const color = d3.interpolateRgb('rgb(0, 0, 0)', 'rgb(255, 255, 255)')(0.5);
    console.log(color); // rgb(128, 128, 128)
    

坐标轴

d3 提供了 4 个方向的轴

  • d3.axisTop(scale)
  • d3.axisBottom(scale)
  • d3.axisLeft(scale)
  • d3.axisRight(scale)

坐标轴需要配合比例尺一起使用。示例如下

// 定义一个比例尺(具体定义方法见前面)
const x = '';
const y = '';
const xAxis = d3.axisBottom(x);
const yAxis = d3.axisLeft(y);
// 在画布中特定位置放置坐标轴
svg.append('g').attr('transform', 'translate(0, 0)').call(xAxis);
svg.append('g').attr('transform', 'translate(0, 0)').call(yAxis);

图形

  • d3.line():折线图
  • d3.area():面积图
  • d3.arc():环形图
  • d3.pie():饼图

使用示例如下

// 折线图
const line = d3
  .line()
  .x((d) => x(d.date))
  .y((d) => y(d.close));

svg
  .append('g')
  .append('path')
  .attr('d', line(data))
  .attr('fill', 'purple')
  .attr('stroke', 'purple');

// 面积图
const area = d3
  .area()
  .x((d) => x(d.date))
  .y0(y(0)) // 该数据可自定义,当其不为 y(0) 的时候,则会从指定的位置开始生成面积,而不是从 y 轴为 0 的地方
  .y1((d) => y(d.close));

svg
  .append('g')
  .append('path')
  .attr('d', area(data))
  .attr('fill', 'purple')
  .attr('stroke', 'purple');

// 环形图
const arc = d3
  .arc()
  .innerRadius(20)
  .outerRadius(30)
  .startAngle(([startAngle, endAngle]) => startAngle)
  .endAngle(([startAngle, endAngle]) => endAngle);

svg
  .append('g')
  .attr('transform', 'translate(30, 30)')
  .append('path')
  .attr('d', arc([0, 2 * Math.PI]));

// 饼图,需要和环形图结合使用
// 生成饼图数据
const pieArcData = d3.pie().value((d) => d.count)(data);
// 生成环形图
const arcPie = d3
  .arc()
  .innerRadius(100)
  .outerRadius(120)
  .padRadius(100)
  .padAngle(2 / 100)
  .cornerRadius(8);

// 生成每一块饼
const arcs = svg
  .append('g')
  .attr('transform', 'translate(150, 120)')
  .selectAll('arc')
  .data(pieArcData)
  .enter()
  .append('g');
// 填充饼
arcs
  .append('path')
  .attr('fill', (d, i) => d3.schemeSet3[i])
  .attr('d', arcPie);

动画

d3 有动画形式有以下几种

  • 依赖于 D3 的过渡,创建一个初始图形,然后开始一个过渡来修改它 stroke-dasharray
  • 依赖于可观察的数据流,每当引用 t 发生变化时重新创建图形,并依赖一个 scrubber 来计时。这种方法比前一种效率低,因为图形是从头开始创建的,但更容易编写。

符号生成器 Symbol

d3 内置了七种符号

  • d3.symbolCircle:圆形
  • d3.symbolCross:十字形
  • d3.symbolDiamond:菱形
  • d3.symbolSquare:正方形
  • d3.symbolStart:星形
  • d3.symbolTriangle:向上的三角形
  • d3.symbolWye:Y 形

使用示例如下

// 直接使用
d3.symbol(symbolCross, size);

// 根据条件使用
d3.symbol()
  .type((d) => {
    return symbolCross;
  })
  .size(100);

几种方法解释

  • symbol.type():设置符号类型
  • symbol.size():设置符号尺寸
  • symbol.context():设置父容器,如果设置了父容器,则会生成 <path> 元素,并添加到父容器中,否则,则生成可用于 <path> 元素的 d 属性值的字符串

可自定义符号类型,具体查看 https://github.com/d3/d3-shape#custom-symbol-types

它是一个具有 draw 方法的对象,该方法接收两个参数 contextsize

  • context:一个可以调用 canvas API 中与 path 相关方法的对象
  • size:设置符号的大小

具体写法可参考 d3 源码中的内置符号来了解

Last Updated:
Contributors: af