注意点
- 左上角为坐标原点
(0,0) - 所有图形渲染出来之后默认在原点,需要手动调整位置,可选择更改
x、y、transform等属性(此操作开始有点晕,熟悉之后就好了)
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()
根据需要进行追加、删除、重新排列元素,以匹配先前通过选择绑定的数据,返回合并后的 enter 和 update 集合
也就是说 join 是一个简化操作:将 data() 返回的 update 和 enter 进行 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:对数据进行标准化,基线是零,上边界线是 1d3.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);
width和margin是一个手动设置的变量,表示图表的总宽度和内边距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 方法的对象,该方法接收两个参数 context 和 size
context:一个可以调用canvas API中与path相关方法的对象size:设置符号的大小
具体写法可参考 d3 源码中的内置符号来了解