d3-shape

可视化通常由离散图形标记组成, 比如 symbols, arcs, linesareas。虽然条形的矩形可以很容易的使用 SVG 或者 Canvas 来生成, 但是其他的比如圆形的扇形以及向心 Catmull-Rom 样条曲线就很复杂。这个模块提供了许多图形生成器以便使用。

D3 的其他特性一样,这些图形也是又数据驱动的: 每个图形生成器都暴露了一个如何将数据映射到可视化表现的访问器。例如你可以通过 scaling 定义一个时间序列的线条生成器以生成图表:

var line = d3.line()
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.value); });

线条生成器可以计算 SVG path 元素的 d 属性:

path.datum(data).attr("d", line);

或者也可以将其渲染到 Canvas 2D 上下文中:

line.context(context)(data);

更多信息参考 Introducing d3-shape.

Installing

NPM 安装: npm install d3-shape. 也可以下载 latest release. 此外还可以从 d3js.orgstandalone library 或作为 D3 4.0 的一部分载入. 支持 AMD, CommonJS 以及基本的标签引入形式,如果使用标签引入则会暴露全局 d3 变量:

<script src="https://d3js.org/d3-path.v1.min.js"></script>
<script src="https://d3js.org/d3-shape.v1.min.js"></script>
<script>

var line = d3.line();

</script>

在浏览器中测试 d3-shape.

API Reference

Arcs

Pie ChartDonut Chart

arc 生成器用来在饼图或圆环图中生成 circular(圆形)annular(环形) 扇形。如果 startend 之间的角度(angular span)差大于 τarc 生成器将会产生一个完整的圆或环。如果小于 τ 则生成的扇形可能有 rounded corners(圆角)angular padding(角度间隙)。弧的中心总是在 ⟨0,0⟩; 可以使用 transform (参考 SVG, Canvas) 来将其移动到指定的位置。

可以与 pie generator 对比,pie 生成器用来计算一组数据作为饼图或圆环图时所需要的角度信息;这些角度信息会被传递给 arc 生成器生成图形。

# d3.arc() <源码>

使用默认的设置创建一个新的 arc 生成器。

# arc(arguments…) <源码>

根据指定的 arguments 生成 arcarguments 是任意的; 它们只是简单地传递到 arc 生成器的访问器函数的对象。例如,根据默认的设置,传入的对象应该包含以下半径和角度信息:

var arc = d3.arc();

arc({
  innerRadius: 0,
  outerRadius: 100,
  startAngle: 0,
  endAngle: Math.PI / 2
}); // "M0,-100A100,100,0,0,1,100,0L0,0Z"

如果半径和角度信息在构建生成器时已经被设置为常量,则不需要传入任何参数:

var arc = d3.arc()
    .innerRadius(0)
    .outerRadius(100)
    .startAngle(0)
    .endAngle(Math.PI / 2);

arc(); // "M0,-100A100,100,0,0,1,100,0L0,0Z"

如果 arc 生成器拥有 context 则这个弧会被作为 path method 的调用序列渲染到对应的上下文中并返回空。否则,返回一个 path data 字符串。

# arc.centroid(arguments…) <源码>

计算由给定 arguments 生成的 generated的中间点 [x, y]. arguments 是任意的,它们会被传递给 arc 生成器的访问器。为了与生成的弧保持一致,访问器必须是确定的。例如,相同的参数返回相同的值。中间点被定义为 (startAngle + endAngle) / 2 和 (innerRadius + outerRadius) / 2。例如:

Circular Sector CentroidsAnnular Sector Centroids

注意,中间点 并不是几何中心,因为几何中心点可能位于弧之外; 这个方法可以用来方便的对 labels 进行定位。

# arc.innerRadius([radius]) <源码>

如果指定了 radius 则将内半径设置为指定的函数或数值并返回当前 arc 生成器。如果没有指定 radius 则返回当前的内半径访问器,默认为:

function innerRadius(d) {
  return d.innerRadius;
}

将内半径设置为函数在生成堆叠的极坐标条形图时非常有用,通常与 sqrt scale 组合。更常见的是将内半径设置为常量用来生成 donut 或者 pie 图。如果外半径小于内半径则内外半径将会被互换。负值被看做 0

# arc.outerRadius([radius]) <源码>

如果指定了 radius 则将外半径设置为指定的函数或数值并返回当前 arc 生成器。如果没有指定 radius 则返回当前的外半径访问器,默认为:

function outerRadius(d) {
  return d.outerRadius;
}

将内半径设置为函数在生成 coxcomb 图或极坐标条形图时非常有用,通常与 sqrt scale 组合。更常见的是将外半径设置为常量用来生成 donut 或者 pie 图。如果外半径小于内半径则内外半径将会被互换。负值被看做 0

# arc.cornerRadius([radius]) <源码>

如果指定了 radius 则将拐角半径设置为指定的函数或数值并返回当前 arc 生成器。如果没有指定 radius 则返回当前的拐角半径访问器,默认为:

function cornerRadius() {
  return 0;
}

如果拐角半径大于 0 则弧度的拐角将会适用指定半径大小的圆进行圆滑。对于扇形,会有两个拐角被圆滑处理,对于环形,所有的四个拐角都会被圆滑处理。拐角处理示意图如下:

Rounded Circular SectorsRounded Annular Sectors

拐角半径不应该大于 (outerRadius - innerRadius) / 2。此外,对于弧长小于等于 π 的弧, 当两个相邻的拐角角相交时,拐角半径可以减小。这种情况更经常发生在内角。参考 arc corners animation 中的插图。

# arc.startAngle([angle]) <源码>

如果指定了 angle 则将起始角度设置为指定的函数或数值并返回当前 arc 生成器。如果没有指定 angle 则返回当前的起始角度访问器,默认为:

function startAngle(d) {
  return d.startAngle;
}

angle 以弧度的形式指定,0 表示 12 点钟方向并且顺时针方向为正。如果 |endAngle - startAngle| ≥ τ 则会绘制一个完整的扇形或圆环。

# arc.endAngle([angle]) <源码>

如果指定了 angle 则将终止角度设置为指定的函数或数值并返回当前 arc 生成器。如果没有指定 angle 则返回当前的终止角度访问器,默认为:

function endAngle(d) {
  return d.endAngle;
}

angle 以弧度的形式指定,0 表示 12 点钟方向并且顺时针方向为正。如果 |endAngle - startAngle| ≥ τ 则会绘制一个完整的扇形或圆环。

# arc.padAngle([angle]) <源码>

如果指定了 angle 则将间隙角度设置为指定的函数或数值,并返回当前 arc 生成器。如果 angle 没有指定则返回当前间隙角度访问器,默认为:

function padAngle() {
  return d && d.padAngle;
}

间隔角度会转换为一个在两个相邻的弧之间的确定的线性距离,定义为 padRadius * padAngle,这个距离在弧的开始和结束处都是相等的。如果弧形成一个完整的圆或环,也就是 |endAngle - startAngle| ≥ τ, 则间隔角度会被忽略。

如果 inner radius 或扇(环)形状的角跨度相对于间隔角度很小,则在相邻的弧之间保持平行的边缘是不可能的。在这种情况下,弧的内边缘可能会靠近成一个点,类似于一个圆形的扇形区域。由于这个原因,间隔角度通常只应用于环形扇区(即当内半径大于 0),如图所示:

Padded Circular SectorsPadded Annular Sectors

使用角度间隔时,推荐的角度间隔为 outerRadius \* padAngle / sin(θ),其中 θ 是所有扇(环)形中角度跨度最小的角度值。例如,如果外半径为 200 像素,间隔角度为 0.02 弧度,则合理的 θ0.04,合理的内半径为 100 像素。参考arc padding animation.

通常情况下,间隔角度不会在 arc 生成器中直接设置,而是通过 pie generator 来计算出一个与具体数据相比合理的间隔角度;参考 pie.padAnglepie padding animation. 如果你使用了一个常量表示间隔角度,则它会倾向于从较小的弧跨度中减去不成比例的,可能会失真.

# arc.padRadius([radius]) <源码>

如果指定了 radius 则将间隔半径设置为指定的函数或数值并返回 arc 生成器。如果没有指定 radius 则返回当前的间隔半径访问器,默认为 null,此时间隔半径通过 sqrt([innerRadius](#arc_innerRadius) * innerRadius + [outerRadius](#arc_outerRadius) * outerRadius) 自动计算。间隔半径决定将两个相邻的扇(环)形分开的固定距离,定义为 padRadius * [padAngle](#arc_padAngle).

# arc.context([context]) <源码>

如果指定了 context 则设置渲染上下文并返回 arc 生成器。如果 context 没有指定则返回当前的上下文,默认为 null。如果上下文非空,则 generated arc 会被渲染到指定的上下文。否则会返回一个表示 arcpath data 字符串。

Pies

pie 生成器不会直接生成图形,但是会计算生成饼图或环形图所需要的角度信息,这些角度信息可以被传递给 arc generator

# d3.pie() <源码>

构建一个新的使用默认配置的 pie 生成器。

# pie(data[, arguments…]) <源码>

根据指定的 data 数组生成一组对象数组,其中每个对象包含每个传入的数据经过计算后的角度信息。可以包含其他的额外 argements,这些额外的参数会直接被传递给当前数据计算后生成的对象或饼图生成器的访问器。返回数组的长度与 data 长度一致,其中第 i 个元素与输入数据中的第 i 个元素对应。返回数组中的每个对象包含以下属性:

这种形式的设计可以兼容 arc 生成器的默认 startAngle, endAnglepadAngle 访问器。角度单位是任意的,但是如果你想将饼图生成器和 arc 生成器结合使用,则应该以弧度的形式指定角度值,其中 12 点钟方向为 0 度并且顺时针方向为正。

给定一个小数据集,下面为如何计算其每个数据的角度信息:

var data = [1, 1, 2, 3, 5, 8, 13, 21];
var arcs = d3.pie()(data);

pie() constructs(构造) 一个默认的 pie 生成器。pie()(data) 为指定的数据集 invokes(调用) 饼图生成器,返回一组对象数组:

[
  {"data":  1, "value":  1, "index": 6, "startAngle": 6.050474740247008, "endAngle": 6.166830023713296, "padAngle": 0},
  {"data":  1, "value":  1, "index": 7, "startAngle": 6.166830023713296, "endAngle": 6.283185307179584, "padAngle": 0},
  {"data":  2, "value":  2, "index": 5, "startAngle": 5.817764173314431, "endAngle": 6.050474740247008, "padAngle": 0},
  {"data":  3, "value":  3, "index": 4, "startAngle": 5.468698322915565, "endAngle": 5.817764173314431, "padAngle": 0},
  {"data":  5, "value":  5, "index": 3, "startAngle": 4.886921905584122, "endAngle": 5.468698322915565, "padAngle": 0},
  {"data":  8, "value":  8, "index": 2, "startAngle": 3.956079637853813, "endAngle": 4.886921905584122, "padAngle": 0},
  {"data": 13, "value": 13, "index": 1, "startAngle": 2.443460952792061, "endAngle": 3.956079637853813, "padAngle": 0},
  {"data": 21, "value": 21, "index": 0, "startAngle": 0.000000000000000, "endAngle": 2.443460952792061, "padAngle": 0}
]

需要注意的是,返回的数组与传入的数据集的次序是一致的,无论数据元素的值大小。

# pie.value([value]) <源码>

如果指定了 value 则设置当前饼图生成器的值访问器为指定的函数或数值,并返回当前饼图生成器。如果没有指定 value 则返回当前的值访问器默认为:

function value(d) {
  return d;
}

当生成饼图时,值访问器会为传入的数据的每个元素调用并传递当前数据元素 d, 索引 i 以及当前数组 data 三个参数。默认的值访问器假设传入的数据每个元素为数值类型,或者可以使用 valueOf 转为数值类型的值。如果你的数据不是简单的数值,你应该指定一个返回数值类型的值访问器。例如:

var data = [
  {"number":  4, "name": "Locke"},
  {"number":  8, "name": "Reyes"},
  {"number": 15, "name": "Ford"},
  {"number": 16, "name": "Jarrah"},
  {"number": 23, "name": "Shephard"},
  {"number": 42, "name": "Kwon"}
];

var arcs = d3.pie()
    .value(function(d) { return d.number; })
    (data);

这与 mapping 类似,在调用饼图生成器之前,对数据进行预处理:

var arcs = d3.pie()(data.map(function(d) { return d.number; }));

访问器的好处是输入数据仍然与返回的对象相关联,从而使访问数据的其他字段变得更容易,例如设置颜色或添加文本标签。

# pie.sort([compare]) <源码>

如果指定了 compare 则将数据比较函数设置为指定的函数并返回饼图生成器。如果没有指定 compare 则返回当前的数据对比函数,默认为 null。如果数据比较函数和值比较函数都为 null 则返回的 arc 会保持数据的次序。否则,返回的结果会安装相应的比较函数进行排序。设置数据对比函数默认会将 value comparator(值比较函数) 设置为 null

compare 函数会传递两个参数 ab, 每个元素都来自输入数据。如果数据 a 对应的扇形在 b 前面,则比较函数应该返回小于 0 的值; 如果 a 对应的扇形在 b 的后面则比较函数应返回大于 0 的值;返回 0 表示 ab 的相对位置不做任何调整。例如根据 name 对生成的扇形数组进行排序:

pie.sort(function(a, b) { return a.name.localeCompare(b.name); });

排序操作不会影响 generated arc array(生成的数组次序), 生成的数据次序与传入的数组次序保持一致。排序操作是通过修改每个生成的元素的起始角度值来实现排序的。

# pie.sortValues([compare]) <源码>

如果指定了 compare 则将 value 比较函数设置为指定的函数并返回当前的饼图生成器。如果没有指定 compare 则返回当前的值比较函数,默认为降序。默认的值比较函数实现形式为:

function compare(a, b) {
  return b - a;
}

如果数据比较函数和值比较函数都为 null 则生成的数组次序与输入数据的次序保持一致。否则,数据会按照数据比较函数进行排序。设置值比较函数默认将 data comparator 设置为 null.

值比较函数与 data comparator 类似,只不过两个参数 ab 是经过 value accessor 计算之后的值,而不是原始的数据元素。如果 a 应该在 b 前则返回小于 0 的值,如果 a 应该在 b 后面则返回大于 0 的值。返回 0 表示 ab 的相对位置不改变。例如根据值进行排序:

pie.sortValues(function(a, b) { return a - b; });

排序操作不会影响 generated arc array(生成的数组次序), 生成的数据次序与传入的数组次序保持一致。排序操作是通过修改每个生成的元素的起始角度值来实现排序的。

# pie.startAngle([angle]) <源码>

如果指定了 angle 则将饼图的布局起始角度设置为指定的函数或数值并返回饼图生成器。如果没有指定则返回当前起始角度访问器默认为:

function startAngle() {
  return 0;
}

起始角度是整个饼图的开始角度,也就是第一个扇区的开始角度。起始角度访问器只会调用一次,并传递当前数据为参数,其中 this 指向 pie generatorangle 的单位是任意的,但是如果要将饼图生成器与弧生成器结合使用则应该以弧度指定,12点钟 方向为 0 度方向,并且顺时针为正。

# pie.endAngle([angle]) <源码>

如果指定了 angle 则将整个饼图的终止角度设置为指定的函数或数值并返回当前饼图生成器。如果没有指定 angle 则返回当前的终止角度访问器。默认为:

function endAngle() {
  return 2 * Math.PI;
}

终止角度也就是整个饼图的结束角度,即最后一个扇区的终止角度。终止角度访问器会被调用一次病传递当前数据,其中 this 指向 pie generator,角度的单位是任意的,但是如果要将饼图生成器与弧生成器结合使用则应该以弧度指定,12点钟 方向为 0 度方向,并且顺时针为正。

终止角度可以被设置为 startAngle ± τ,这样就能保证 |endAngle - startAngle| ≤ τ.

# pie.padAngle([angle]) <源码>

如果指定了 angle 则将饼图扇形之间的间隔设置为指定的函数或数值,并返回当前饼图生成器。如果没有指定 angle 则返回当前默认的间隔角度访问器,默认为:

function padAngle() {
  return 0;
}

这里的间隔角度也就是两个相邻的扇形之间的间隔。间隔角度的总和等于指定的角度乘以输入数据数组中的元素数量,最大为 |endAngle - startAngle|;然后,剩余的间隔按比例按比例分配,这样每个弧的相对面积就会被保留下来。参考 pie padding animation 获取更详细的说明。间隔访问器只会被调用一次,并传递当前数据集,其中 this 上下文指向 pie generator。角度的单位是任意的,但是如果要将饼图生成器与弧生成器结合使用则应该以弧度指定。

Lines

Line Chart

line 生成器可以用来生成线条图需要的 splinepolyline。线条也可以被用在其他的可视化类型中,比如 hierarchical edge bundling

# d3.line() <源码>

使用默认的设置构造一个 line 生成器。

Constructs a new line generator with the default settings.

# line(data) <源码>

根据指定的 data 数组生成一个线条。根据与线条生成器的关联的 curve,输入数据 data 可能需要根据 x 值进行排序。如果线条生成器有 context,则线条会通过 path method 被渲染到指定的上下文中,否则返回一个 path data 字符串。

# line.x([x]) <源码>

如果指定了 x 则将 x 访问器设置为指定的函数或数值并返回当前 line 生成器。如果没有指定 x 则返回当前 x 访问器,默认为:

function x(d) {
  return d[0];
}

在线条被 generated 时,x 访问器将会为输入数据中每一个 defined 的元素进行调用,并传入当前元素 d, 当前索引 i 以及当前所有元素 data 三个参数。默认的 x 访问器假设输入的数据为一个数值类型的二维数组。如果你的数据是其他的格式或者你在渲染前想进一步处理,则应该指定一个自定义的访问器。比如,如果你的 xtime scale 并且 ylinear scale:

var data = [
  {date: new Date(2007, 3, 24), value: 93.24},
  {date: new Date(2007, 3, 25), value: 95.35},
  {date: new Date(2007, 3, 26), value: 98.84},
  {date: new Date(2007, 3, 27), value: 99.92},
  {date: new Date(2007, 3, 30), value: 99.80},
  {date: new Date(2007, 4,  1), value: 99.47},];

var line = d3.line()
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.value); });

# line.y([y]) <源码>

如果指定了 y 则将 y 访问器设置为指定的函数或数值并返回当前 line 生成器。如果没有指定 y 则返回当前 y 访问器,默认为:

function y(d) {
  return d[1];
}

在线条被 generated 时,x 访问器将会为输入数据中每一个 defined 的元素进行调用,并传入当前元素 d, 当前索引 i 以及当前所有元素 data 三个参数。默认的 x 访问器假设输入的数据为一个数值类型的二维数组。参考 line.x 获取更多信息。

# line.defined([defined]) <源码>

如果指定了 defined 则将已定义的访问器设置为指定的函数或布尔值。如果没有指定 defined 则返回当前默认的已定义的访问器,默认为:

function defined() {
  return true;
}

默认的访问器假设输入数据总是被定义的。当线条被 generated 时,定义的访问器会为输入数据的每个元素定义,并传递当前数据元素 d, 索引 i 以及数组 data 作为三个元素。如果给定的元素有定义的(i.e. 已定义的访问器为当前元素返回真值), 则 xy 访问器会对其进行评估并将当前点添加到线段中生成一个新的线段。否则会跳过当前数据元素,并结束当前线段,在下一个点重新开始一个新的线段。此时的结果会如下所示:

Line with Missing Data

注意,如果一个线段仅由一个点组成,那么它可能看起来是不可见的,除非用圆形或方形的 line caps(线帽) 呈现。此外一些曲线比如 curveCardinalOpen 如果包含多个点会被渲染成为一个可见的线段。

# line.curve([curve]) <源码>

如果指定了 curve 则表示设置当前的 curve factory(曲线插值方法) 并返回线条生成器。如果没有指定 curve 则返回当前的线条插值方式,默认为 curveLinear.

# line.context([context]) <源码>

如果指定了 context 则设置上下文并返回当前线条生成器。如果没有指定 context 则返回当前的上下文,默认为 null。如果上下文不为空,则线条生成器会调用 path method 被渲染到当前上下文中。否则会返回一个 path data 字符串用来表示生成的线条。

# d3.lineRadial() <源码>

Radial Line

使用默认的设置构造一个新的 radial line(径向线条) 生成器。径向线条生成器类似于笛卡尔坐标系下的 line generator,只不过 xy 访问器被替换成了 angleradius 访问器。径向线条的生成总是相对于 ⟨0,0⟩,但是你可以使用坐标变换调整其位置。参考 SVG, Canvas 的坐标变换。

# lineRadial(data) <源码>

等价于 line.

# lineRadial.angle([angle]) <源码>

等价于 line.x, 只不过此访问器返回的是弧度,0 度在 - y (12 点钟方向).

# lineRadial.radius([radius]) <源码>

等价于 line.y,只不过此访问器返回一个半径值,也就是距离 ⟨0,0⟩ 的距离.

# lineRadial.defined([defined])

等价于 line.defined.

# lineRadial.curve([curve]) <源码>

等价于 line.curve. 注意 curveMonotoneXcurveMonotoneY 插值方式不被推荐用在径向线条布局中,因为这两种插值方式假设 xy 是单调的。

# lineRadial.context([context])

等价于 line.context.

Areas

Area ChartStacked Area ChartDifference Chart

area generator(区域生成器) 用来在 area 图中生成区域图。一个区域图由两条边界 lines 定义,可以是曲线或折线。通常情况下两条边界线共享一个 x-values (x0 = x1),仅仅是 y-value (y0y1) 不一样。大多数情况下,y0 会被定义为一个常量 zero. 第一条线 (也就是 上侧线) 由 x1y1 定义渲染。而第二条线 (也就是 基线) 由 x0y0 定义渲染。有了 curveLinear curve,就可以生成一个顺时针方向的多边形。

# d3.area() <源码>

使用默认的设置构建一个区域生成器。

# area(data) <源码>

根据指定的一组数据 data。根据这个区域生成器的相关 curve ,给定的输入数据可能需要在传递给区域生成器之前按 x- 值排序。如果区域生成器被指定了 context 则区域图会调用 path method 被渲染到指定的上下文上,并且这个方法返回 void。否则会返回一个表示 path data 的字符串。

# area.x([x]) <源码>

如果指定了 x 则设置 x0x 并且设置 x1null,返回当前区域生成器。如果没有指定 x 则返回当前 x0 访问器。

# area.x0([x]) <源码>

如果指定看 x 则将 x0 访问器设置为指定的函数或数值并返回当前区域生成器。如果没有指定 x 则返回当前 x0 生成器, 默认为:

function x(d) {
  return d[0];
}

在区域被 generatedx0 访问器会依次为输入的数据元素调用。并传递当前数据 d, 索引 i 以及数据数组 data 三个参数。默认的 x0 访问器假设输入的数据为一个二元数值数组。如果你的数据是其他的不同的格式,或者需要在渲染前进行转换则你需要设置一个自定义的访问器。比如 xtime scale 并且 ylinear scale:

var data = [
  {date: new Date(2007, 3, 24), value: 93.24},
  {date: new Date(2007, 3, 25), value: 95.35},
  {date: new Date(2007, 3, 26), value: 98.84},
  {date: new Date(2007, 3, 27), value: 99.92},
  {date: new Date(2007, 3, 30), value: 99.80},
  {date: new Date(2007, 4,  1), value: 99.47},];

var area = d3.area()
    .x(function(d) { return x(d.date); })
    .y1(function(d) { return y(d.value); })
    .y0(y(0));

# area.x1([x]) <源码>

如果制定了 x 则设置 x1 访问器为指定的函数或数值并返回区域生成器。如果 x 没有指定则返回当前的 x1 访问器,默认为 null 表示先前计算的 x0 值应该为 x1 值重用。

当一个区域图被 generated 时,x1 访问器将会为每个定义的元素调用。并传递当前元素 d, 索引 i 以及数据数组三个参数。参考 area.x0 获取更多信息。

# area.y([y]) <源码>

如果指定了 y 则设置 y0y 并设置 y1null, 返回区域生成器。如果 y 没有被指定则返回当前的 y0 访问器。

# area.y0([y]) <源码>

如果指定了 y 则设置 y0 访问器为指定的函数或数值并返回当前区域生成器。如果没有指定 y 则返回当前 y0 访问器默认为:

function y() {
  return 0;
}

在区域被 generated 时,y0 访问器会为每个输入的元素调用并传递当前数据元素 d, 当前索引 i 以及数据数组 data。参考 area.x0 获取更多信息。

# area.y1([y]) <源码>

如果指定了 y 则将 y1 访问器设置为指定的函数或数值并返回当前区域生成器。如果没有指定 y 则返回当前 y1 访问器默认为:

function y(d) {
  return d[1];
}

可以使用空的访问器以表明 y0 会被 y1 复用,y1 访问器会为每个定义的元素调用并传递当前数据元素 d, 索引 i 以及当前数组 data 三个参数。参考 area.x0 获取更多信息。

# area.defined([defined]) <源码>

如果指定了 defined 则将定义访问器设置为指定的函数或布尔值并返回区域生成器。如果没有指定 defined 则返回当前定义访问器默认为:

function defined() {
  return true;
}

默认的访问器假设输入数据都是定义的。当区域图被生成时,已经定义的访问器会为每个数据元素调用,并传递当前数据元素 d, 索引 i 以及数组 data 三个参数。如果给定的元素时定义的(i.e. 访问器返回真值),x0, x1, y0y1 访问器会为当前元素评估然后将坐标加入到区域分段中。否则会跳过当前元素并结束当前区域分段,并为下一个定义的数据新开一个分段。此时的结果会表现为分段的区域图:

Area with Missing Data

需要注意的是,如果只包含一个点则可能看起来是不可见的除非用圆形或方形的 line caps 呈现。此外,一些曲线,如 curveCardinalOpen ,如果它包含多个点,则只呈现一个可见的段。

# area.curve([curve]) <源码>

如果指定了 curve 则将 curve factory 设置为指定的 curve 并返回区域生成器。如果没有指定 curve 则返回默认的插值方式,默认为 curveLinear

# area.context([context]) <源码>

如果指定了 context 则将区域生成器的上下文设置为指定的 context。如果没有指定 context 则返回当前上下文,默认为 null。如果上下文非 null, 在 generated area 时会调用 path method 将区域渲染到指定的上下文中,否则返回一个表示轮廓的 path data 字符串。

# area.lineX0() <源码>
# area.lineY0() <源码>

返回一个新的 line generator, 内置了当前区域生成器定义的 defined accessor, curvecontext。线条的 x-accessor 为区域生成器的 x0-accessor, 线条的 y-accessor 为区域生成器的 y0-accessor.

# area.lineX1() <源码>

返回一个新的 line generator 内置了当前区域生成器定义的 defined accessor, curvecontext.线条的 x-accessor 为区域生成器的 x1-accessor, 线条的 y-accessor 为区域生成器的 y0-accessor.

# area.lineY1() <源码>

返回一个新的 line generator,内置了区域生成器的 defined accessor, curve and context. 线条的 x-accessor 为区域生成器的 x0-accessor, 线条的 y-accessor 为区域生成器的 y1-accessor.

# d3.areaRadial() <源码>

Radial Area

使用默认的设置构造一个新的径向区域生成器。径向区域生成器类似于标准的笛卡尔坐标系下的 area generator 只不过 xy 访问器被替换为 angleradius 访问器。径向区域图总是相对于 ⟨0,0⟩。你可以使用坐标变换将其平移到指定的位置。参考 SVG, Canvas 的坐标变换。

# areaRadial(data)

等价于 area.

# areaRadial.angle([angle]) <源码>

等价于 area.x, 只不过返回值为弧度,其中 0 度位于 -y (12 点钟方向).

# areaRadial.startAngle([angle]) <源码>

等价于 area.x0, 只不过访问器返回弧度值,其中0 度位于 -y (12 点钟方向). 注意:通常使用角度,而不是设置单独的起始和结束角。

# areaRadial.endAngle([angle]) <源码>

等价于 area.x1, 只不过访问器返回弧度值,其中0 度位于 -y (12 点钟方向). 注意:通常使用角度,而不是设置单独的起始和结束角。

# areaRadial.radius([radius]) <源码>

等价于 area.y, 只不过访问器返回的是距离 ⟨0,0⟩ 的距离。

# areaRadial.innerRadius([radius]) <源码>

等价于 area.y0, 只不过访问器返回的是距离 ⟨0,0⟩ 的距离。

# areaRadial.outerRadius([radius]) <源码>

等价于 area.y1, 只不过访问器返回的是距离 ⟨0,0⟩ 的距离。

# areaRadial.defined([defined])

等价于 area.defined.

# areaRadial.curve([curve]) <源码>

等价于 area.curve. 注意不推荐使用 curveMonotoneXcurveMonotoneY 因为这两种曲线假设 xy 维度是单调的, 不适合用于径向区域图.

# areaRadial.context([context])

等价于 line.context.

# areaRadial.lineStartAngle() <源码>
# areaRadial.lineInnerRadius() <源码>

返回一个新的 radial line generator,内置当前径向区域图的 defined accessor, curvecontext。线条的 angle accessor 为区域图的 start angle accessor, 线条的 radius accessor 为区域图的 inner radius accessor.

# areaRadial.lineEndAngle() <源码>

返回一个新的 radial line generator,内置区域图的 defined accessor, curvecontext。线条的 angle accessor 为区域图的 end angle accessor, 线条的 radius accessor 为区域图的 inner radius accessor.

# areaRadial.lineOuterRadius() <源码>

返回一个新的 radial line generator,内置区域图的 defined accessor, curvecontext. 线条的 angle accessor 为区域图的 start angle accessor, 线条的 radius accessor 为区域图的 outer radius accessor.

Curves

lines 被定义为一系列二维点 [x, y],areas 类似的可以由顶线和基线定义,但是还有一个任务就是把这些离散的点转换为连续的线条: 例如如何在这些点之间进行插值,插值的方式有很多种。

插值曲线通常不会直接使用,而是传递给 line.curvearea.curve。例如:

var line = d3.line()
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.value); })
    .curve(d3.curveCatmullRom.alpha(0.5));

# d3.curveBasis(context) <源码>

basis

使用指定的控制点生成一个三次 basis spline(样条曲线)。第一个和最后一个点会被分成三个重复的点,这样就能保证线条经过第一个和最后一个点。并且曲线与第一个和第二个点之间的连线相切,同时与最后一个与倒数第二个点连线相切。

# d3.curveBasisClosed(context) <源码>

basisClosed

使用指定的控制点生成一个闭合的三次 basis spline。当一个线段结束时,前三个控制点被重复,产生一个连续性的闭环。

# d3.curveBasisOpen(context) <源码>

basisOpen

使用指定的控制点生成一个三次 basis spline。与 basis 不同,第一个和最后一个控制点不会被重复,这条曲线通常不会与这些点相交。

# d3.curveBundle(context) <源码>

bundle

使用指定的控制点产生一个可以校正调整的三次 basis spline,校正系数根据曲线的 beta 系数确定,默认为 0.85。这种曲线通常用在 hierarchical edge bundling 中来消除视觉混淆。这个算法是 Danny HoltenHierarchical Edge Bundles: Visualization of Adjacency Relations in Hierarchical Data 中提出的。这种曲线生成方式不会实现 curve.areaStartcurve.areaEnd,它的设计是用来和 d3.line 结合的,而不是 d3.area

# bundle.beta(beta) <源码>

根据指定的 beta 设置曲线的校正系数,系数范围为 [0, 1] 用来表示绑定强度。如果 beta0 则会在第一个和最后一个点之间生成一个直线,如果 beta1,则会生成一个标准的 basis。例如:

var line = d3.line().curve(d3.curveBundle.beta(0.5));

# d3.curveCardinal(context) <源码>

cardinal

使用指定的控制点生成一条三次 cardinal spline 曲线,默认 tension0.

# d3.curveCardinalClosed(context) <源码>

cardinalClosed

使用指定的控制点生成一条闭合的三次 cardinal spline 曲线。默认 tension0.

# d3.curveCardinalOpen(context) <源码>

cardinalOpen

使用指定的控制点生成一条三次 cardinal spline 曲线,与 curveCardinal 不同,生成的曲线不利用第一个和最后一个点。默认 tension0.

# cardinal.tension(tension) <源码>

使用指定的处于 [0, 1] 的 tension 系数设置曲线的张力,张力确定了切线的长度:张力为 1 等价于 curveLinear,张力为 0 等价于 Catmull–Rom。例如:

var line = d3.line().curve(d3.curveCardinal.tension(0.5));

# d3.curveCatmullRom(context) <源码>

catmullRom

使用指定的控制点和默认值为 0.5alpha 值生成一条 Catmull–Rom 曲线。曲线的详细介绍参考 On the Parameterization of Catmull–Rom Curves

# d3.curveCatmullRomClosed(context) <源码>

catmullRomClosed

使用指定的控制点和默认值为 0.5alpha 值生成一条闭合的 Catmull–Rom 曲线。

# d3.curveCatmullRomOpen(context) <源码>

catmullRomOpen

使用指定的控制点和默认值为 0.5alpha 值生成一条 Catmull–Rom 曲线。与 curveCatmullRom 不同的是所生成的曲线不经过第一个和最后一个控制点。

# catmullRom.alpha(alpha) <源码>

使用指定的 alpha 值([0, 1]) 返回一条 Catmull–Rom 生成器。如果 alpha0 则等价于 curveCardinal,如果 alpha1 则会生成 chordal 曲线,如果 alpha0.5 则会生成 centripetal spline。例如:

var line = d3.line().curve(d3.curveCatmullRom.alpha(0.5));

# d3.curveLinear(context) <源码>

linear

通过指定的点产生折线。

# d3.curveLinearClosed(context) <源码>

linearClosed

产生闭合折线

# d3.curveMonotoneX(context) <源码>

monotoneX

产生一条在 y 方向保持单调性的曲线,假设在 x 方向是单调的。曲线具体描述:A simple method for monotonic interpolation in one dimension

# d3.curveMonotoneY(context) <源码>

monotoneY

产生一条在 x 方向保持单调性的曲线,假设在 y 方向是单调的。曲线具体描述:A simple method for monotonic interpolation in one dimension

# d3.curveNatural(context) <源码>

natural

产生一条 自然的三次样条曲线,其二阶导数在端点设为零。

# d3.curveStep(context) <源码>

step

产生一个分段常数函数 (阶梯函数),由水平和垂直的交替线组成。y 值在每一对相邻 x 值的中点处发生变化。

# d3.curveStepAfter(context) <源码>

stepAfter

产生一个分段常数函数 (阶梯函数),由水平和垂直的交替线组成。y 值在 x 值之后发生变化。

# d3.curveStepBefore(context) <源码>

stepBefore

产生一个分段常数函数 (阶梯函数),由水平和垂直的交替线组成。y 值在 x 值之前发生变化。

Custom Curves

Curves 通常不会直接使用,而是传递给 line.curvearea.curve. 但是,如果你对内置的曲线不满意的话可以使用如下接口定义自己的曲线实现来替代内置的曲线。你也可以使用这个带有内置曲线类型的低级接口作为线和面积生成器的替代选择。

# curve.areaStart() <源码>

指明新区域段的开始。每个区域段恰好由两个 line segments 组成 : 顶线,基线点以相反的顺序排列的基线。

# curve.areaEnd() <源码>

指明当前区域段的结束。

# curve.lineStart() <源码>

指明新线段的开始。后面可以跟着零个或多个 points

# curve.lineEnd() <源码>

指明当前线段的结束。

# curve.point(x, y) <源码>

指明当前线段中具有给定 xy 值的新点。

Tidy Tree

link 用来生成从一个源点到目标点的光滑的三次贝塞尔曲线。曲线在起点和终点的切线要么是 vertical,要么是 horizontal,要么是 radial的。

# d3.linkVertical() <源码>

返回一个新的 link 生成器,生成的曲线在曲线的终点和起点处的切线是垂直方向的。例如在 tree diagram 中对 links 进行可视化时,可以定义为:

var link = d3.linkVertical()
    .x(function(d) { return d.x; })
    .y(function(d) { return d.y; });

# d3.linkHorizontal() <源码>

返回一个新的 link 生成器,生成的曲线在曲线的终点和起点处的切线是水平方向的。例如在 tree diagram 中对 links 进行可视化时,可以定义为:

var link = d3.linkHorizontal()
    .x(function(d) { return d.y; })
    .y(function(d) { return d.x; });

# link(arguments…) <源码>

根据指定的 arguments 生成 linksarguments 是任意的。它们会被直接传递给 link 生成器的访问函数。例如,使用的默认设置时,期望的参数为:

link({
  source: [100, 100],
  target: [300, 300]
});

# link.source([source]) <源码>

如果指定了 source 则将 source 访问器设置为指定的函数并返回当前 link 生成器。如果没有指定 source 则返回当前的 source 访问器,默认为:

function source(d) {
  return d.source;
}

# link.target([target]) <源码>

如果指定了 target 则将 target 访问器设置为指定的函数并返回当前 link 生成器。如果没有指定 target 则返回当前的 target 访问器,默认为:

function target(d) {
  return d.target;
}

# link.x([x]) <源码>

如果指定了 x 则将 x 访问器设置为指定的函数或数值,并返回当前 link 生成器。如果 x 没有指定则返回当前 x 访问器,默认为:

function x(d) {
  return d[0];
}

# link.y([y]) <源码>

如果指定了 y 则将 y 访问器设置为指定的函数或数值,并返回当前 link 生成器。如果 y 没有指定则返回当前 y 访问器,默认为:

function y(d) {
  return d[1];
}

# link.context([context]) <源码>

如果指定了 context,则设置上下文并返回当前 link 生成器。如果没有指定 context 则返回当前的上下文,默认为 null。如果上下文非空,则 生成的 link 会被渲染到指定的上下文中。否则会返回 path data字符串。参考 d3-path.

# d3.linkRadial() <源码>

返回一个新的径向 link 生成器。例如在 tree diagram 中对 links 进行可视化时,可以定义为:

var link = d3.linkRadial()
    .angle(function(d) { return d.x; })
    .radius(function(d) { return d.y; });

# linkRadial.angle([angle]) <源码>

等价于 link.x, 只不过访问器返回的是弧度值,其中 0 度为 12 点钟方向。

# linkRadial.radius([radius]) <源码>

等价于 link.y, 只不过访问器返回的是半径: 到 ⟨0,0⟩ 的距离。

Symbols

符号提供了几种用来表示分类的形状。符号的坐标总是位于 ⟨0,0⟩, 需要使用 transform 将其移动到指定的位置(参考: SVG, Canvas)。

# d3.symbol() <源码>

使用默认的设置构造一个新的符号生成器。

# symbol(arguments…) <源码>

使用指定的 arguments 生成一个符号。arguments 是任意的。它们会被直接传递给符号生成器的访问器。例如使用默认的设置,没有任何参数的情况下生成的是一个 64 平方像素的区域。如果符号生成器拥有 context 则符号会被渲染到此上下文中。否则会返回一个 path data 字符串。

# symbol.type([type]) <源码>

如果指定了 type 则将符号的类型设置为指定的函数或符号类型并返回符号生成器。如果没有指定 type 则返回当前的类型访问器,默认为:

function type() {
  return circle;
}

参考 symbols 获取内置的符号类型。如果需要实现自己的符号类型,可以传递对象symbolType.draw

# symbol.size([size]) <源码>

如果指定了 size 则将符号的尺寸设置为指定的函数或数值并返回符号生成器。如果没有指定 size 则返回当前的尺寸访问器,默认为:

function size() {
  return 64;
}

当使用大小对散点图进行编码时传递一个函数是非常有用的。如果你希望符号的大小适应给定的包裹矩形,而不是指定面积则可以使用 SVG’s getBBox.

# symbol.context([context]) <源码>

如果指定了 context 则将上下文设置为指定的上下文并返回符号生成器。如果没有指定 context 则返回当前的上下文,默认为 null。如果上下文非空则 在生成符号会被渲染到指定的上下文中。否则会返回一个表示符号的 path data 字符串。

# d3.symbols

一组包含内置符号的数组:circle, cross, diamond, square, star, triangle, 和 wye. 如果您希望对分类数据使用形状编码,那么这对于构造 ordinal scale 比例尺的范围时非常有用。

# d3.symbolCircle <源码>

圆形

# d3.symbolCross <源码>

希腊十字符号类型,臂长相等

# d3.symbolDiamond <源码>

菱形

# d3.symbolSquare <源码>

正方形

# d3.symbolStar <源码>

五角星

# d3.symbolTriangle <源码>

向上的三角形

# d3.symbolWye <源码>

Y 形符号

# d3.pointRadial(angle, radius) <源码>

返回给定 angle (以弧度表示)以及 radius 的点的坐标 [x, y],在 12点 处为 0, 正方向为顺时针,以及给定的半径。

Custom Symbol Types

符号类型不能直接使用,而是需要传递给 symbol.type。但是你可以使用低级接口来定义自己的符号来代替内置的符号类型。也可以使用这个低级接口作为符号生成器的替代。

# symbolType.draw(context, size)

将符号类型以指定的 size 渲染到指定的 contextcontext 实现了 CanvasPathMethods 接口。(注意,这是 CanvasRenderingContext2D 的一个子集)

Stacks

Stacked Bar ChartStreamgraph

有些形状类型可以堆叠,将一个形状与另一个邻近放置。例如,月销售条形图可以按照产品类别分为多个系列的条形图并垂直堆放。相当于将条形图按照类别(比如产品类型)细分并使用颜色进行编码。

堆叠图可以同时显示整体值和每个类别的值; 但是对不同类别之间进行对比是比较困难的,因为只有底部是对齐的。所以需要选择合适的 stack order(堆叠次序) 并可以考虑使用 streamgraph。(参考 grouped charts.)

pie generator 类似,堆叠布局生成器不会直接产生形状。它是用来计算堆叠数据的,然后传给 area generator 或者直接使用(定位条形图)。

# d3.stack() <源码>

使用默认的设置构造一个新的堆叠布局生成器。

# stack(data[, arguments…]) <源码>

根据指定的数据数组 data 生成一个堆叠布局,返回形式为序列数组。可以传递任意 arguments,它们会被直接传递给访问器。

Generates a stack for the given array of data, returning an array representing each series. Any additional arguments are arbitrary; they are simply propagated to accessors along with the this object.

返回的序列由 keys accessor 决定。每个序列 i 对应第 ikey。每个序列都是一组点数组,每个点 j 表示输入数据中的第 j 个元素。最后每个点都会被表示为一个数组 [*y0*, *y1*], 其中 y0 表示这个点的下限值(基线),y1 表示这个点的上限值(顶线); y0y1 之间的差值对应当前点的计算 value。每个系列的 keyseries.key 对应, 并且 index 等于 series.index. 每个点的输入数据元素对应 point.data.

例如,考虑如下的表示几种水果的月销售数据的表格:

Month Apples Bananas Cherries Dates
1/2015 3840 1920 960 400
2/2015 1600 1440 960 400
3/2015 640 960 640 400
4/2015 320 480 640 400

JavaScript 中可以表示为对象:

var data = [
  {month: new Date(2015, 0, 1), apples: 3840, bananas: 1920, cherries: 960, dates: 400},
  {month: new Date(2015, 1, 1), apples: 1600, bananas: 1440, cherries: 960, dates: 400},
  {month: new Date(2015, 2, 1), apples:  640, bananas:  960, cherries: 640, dates: 400},
  {month: new Date(2015, 3, 1), apples:  320, bananas:  480, cherries: 640, dates: 400}
];

使用这个数据创建一个堆叠布局:

var stack = d3.stack()
    .keys(["apples", "bananas", "cherries", "dates"])
    .order(d3.stackOrderNone)
    .offset(d3.stackOffsetNone);

var series = stack(data);

返回的结果是一个包含每个 series 的数组。每个系列在每个月都对应一个数据点,每个点都有下限值和上限值用来表示基线和顶线:

[
  [[   0, 3840], [   0, 1600], [   0,  640], [   0,  320]], // apples
  [[3840, 5760], [1600, 3040], [ 640, 1600], [ 320,  800]], // bananas
  [[5760, 6720], [3040, 4000], [1600, 2240], [ 800, 1440]], // cherries
  [[6720, 7120], [4000, 4400], [2240, 2640], [1440, 1840]], // dates
]

每个序列通常会被传递给 area generator 来渲染出区域图,或者直接用来绘制条形图。

# stack.keys([keys]) <源码>

如果指定了 keys 则将 keys 访问器设置为指定的函数或数组,并返回当前堆叠布局生成器。如果没有指定 keys 则返回当前的 keys 访问器,默认为空数组。一个序列(一层) 对应一个 keykeys 通常是字符串,但是也可以是任意值。系列的 key 会被直接传递给 value accessor 以计算每个数据点的值。

# stack.value([value]) <源码>

如果指定了 value 则将值访问器设置为指定的函数或数值并返回当前堆叠布局生成器。如果没有指定则返回当前的值访问器,默认为:

function value(d, key) {
  return d[key];
}

因此,默认情况下堆叠布局生成器假设输入数据是一个对象数组,每个对象都包含了一个值为数值类型的属性。参考 stack

# stack.order([order]) <源码>

如果指定了 order 则将顺序访问器设置为指定的函数或数组并返回当前堆叠布局生成器。如果没有指定 order 则返回当前顺序访问器默认为 stackOrderNone;也就是使用 key accessor 指定的次序。参考 stack orders 获取内置顺序。

如果 order 为函数则会传递生成的系列数组,并且必须返回数组。例如默认的顺序访问器被定义为:

function orderNone(series) {
  var n = series.length, o = new Array(n);
  while (--n >= 0) o[n] = n;
  return o;
}

堆叠次序是在计算 offset 之前进行的,因此在计算次序时每个点的下限都为 0。每个序列的索引属性在计算完次序之后才会被设置。

# stack.offset([offset]) <源码>

如果指定了 offset 则将偏移访问器设置为指定的函数或数组并返回当前堆叠布局。如果 offset 没有指定则返回当前的偏移访问器,默认为 stackOffsetNone; 默认会生成以 0 为基线的堆叠图,参考 stack offsets 了解内置的偏移。

如果 offset 为函数,则会传递系列数组以及顺序索引。偏移函数负责计算更新每个数据点的上下限值。例如默认的偏移被定义为:

function offsetNone(series, order) {
  if (!((n = series.length) > 1)) return;
  for (var i = 1, s0, s1 = series[order[0]], n, m = s1.length; i < n; ++i) {
    s0 = s1, s1 = series[order[i]];
    for (var j = 0; j < m; ++j) {
      s1[j][1] += s1[j][0] = s0[j][1];
    }
  }
}

Stack Orders

堆叠次序通常不会直接使用,而是传递给 stack.order.

# d3.stackOrderAscending(series) <源码>

根据每个序列的值的和进行排序,总和最小的位于最底部。

# d3.stackOrderDescending(series) <源码>

根据每个序列的值的和进行排序,总和最大的位于最底部。

# d3.stackOrderInsideOut(series) <源码>

根据每个序列的值的和进行排序,值大的会被排列在内侧,值小的会被排列在外侧。建议将此排列方式与 wiggle offset 结合使用生成流图。参考 Stacked Graphs—Geometry & Aesthetics 获取更多信息。

# d3.stackOrderNone(series) <源码>

返回给定的序列顺序 [0,1,…n - 1],其中 n 是序列中的元素个数。因此与 key accessor 的顺序一致。

# d3.stackOrderReverse(series) <源码>

返回给定系列顺序 [n - 1, n - 2, … 0],其中 n 是序列中的元素个数。因此与 key accessor 的顺序相反。

Stack Offsets

堆叠偏移通常不会直接使用,而是传递给 stack.offset 来使用。

# d3.stackOffsetExpand(series, order) <源码>

应用零基线,对每个点的值进行规范化,使顶线始终为 1

# d3.stackOffsetDiverging(series, order) <源码>

正值堆叠在零以上,而负值 stacked below zero(零下堆叠)

# d3.stackOffsetNone(series, order) <源码>

0 为基线。

# d3.stackOffsetSilhouette(series, order) <源码>

将基线向下移动,保持流图的中心始终为零。

# d3.stackOffsetWiggle(series, order) <源码>

调整基线,以最小化堆叠图的摆动。推荐使用这个偏移方式创建流图并结合顺序排列方式: inside-out order 使用。参考 Stacked Graphs—Geometry & Aesthetics 获取更多信息。

最后更新: 2019-5-18 18:11:02
本站功能逐步完善中,如果您对本站有好的建议或者意见,欢迎留言。 取 消 确 定