一个简单的 D3.js 面积图
我们来看:
| X | 是 |
|---|---|
| 0 | 10 |
| 1 | 15 |
| 2 | 35 |
| 3 | 20 |
并制作:
D3.js 非常复杂,我觉得 D3.js wiki 上的示例解释得太少,内容又太多。在这个示例中,我将向您展示如何创建一个我能想到的最简单的面积图。如果您想直接查看“答案”,请参阅完整的 JavaScript 代码。
D3.js 不是一个图表库,而是一个图表组件库。这个库感觉像是 SVG 和数据操作的混合体,并添加了一些额外的功能。虽然它非常灵活,但这种灵活性是以复杂性为代价的。让我们深入了解一下。
要构建图表,我们需要:数据、SVG 容器、边距、X 轴、Y 轴、区域形状本身,以及一些 CSS 来使其看起来美观。
数据
我们不会使用 TSV 或 CSV 加载器,也不会使用任何回调函数。数据就在这里,简单明了。
var data = [
{ x: 0, y: 10, },
{ x: 1, y: 15, },
{ x: 2, y: 35, },
{ x: 3, y: 20, },
];
SVG
D3 使用 SVG(可缩放矢量图形)来绘制形状。虽然可以<svg>动态创建新标签,但我还是将以下代码添加到了 HTML 源代码中。
<svg id="area" />
边际
D3 中的图表没有边距,但 D3 的主要作者经常提到定义常规边距。其思路是设置一些边距,并定义一个 SVG 组(即 `<div>`g标签),将其设置为与这些边距边界相符。代码仅将 `<div>` 组标签视为可绘制区域。
var margin = {top: 20, right: 20, bottom: 40, left: 50},
width = 575 - margin.left - margin.right,
height = 350 - margin.top - margin.bottom;
轴
为了以可缩放的方式绘制数据,D3 需要能够将数据(例如,x=0,y=10)映射到像素位置。我们必须获取 X 轴数据并将其设置到坐标轴上,使得 X 轴的最大值(即 3)与图表区域的像素宽度相匹配。由于 D3 具有很高的灵活性,这意味着 X 轴和 Y 轴必须独立定义。
在数学课上,你可能学过 X 代表定义域,Y 代表值域。遗憾的是,D3 也把定义域/值域的概念应用到了坐标轴上。我们需要把 X 轴的数据(0-3)看作是定义域,把图表的水平方向(0- width)看作是值域。同样的道理也适用于 Y 轴(0-35 应用于图表的垂直方向)。
x你可以把 ` and`变量看作y是转换函数,它们接受一个域值并将其转换为像素大小。`and`xAxis和 `or`yAxis则指示坐标轴应该放在哪里。
var x = d3.scale.linear()
.domain([0, d3.max(data, function(d) { return d.x; })])
.range([0, width]);
var y = d3.scale.linear()
.domain([0, d3.max(data, function(d) { return d.y; })])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
区域
面积函数将每个数据点(例如 (2, 35))转换为描述形状的信息。每个数据点对应一个 x 坐标、一个上 y 坐标y1和一个下 y 坐标y0。这里比较特殊的是,下 y 坐标y0被设置为常量height。当你了解 SVG 是相对于图形左上角定位时,这一点就很容易理解了。任何“向下”的距离都是正数,因此正数height表示图形的底部。
var area = d3.svg.area()
.x(function(d) { return x(d.x); })
.y0(height)
.y1(function(d) { return y(d.y); });
把所有东西整合起来
到目前为止,我们除了定义一些数据和函数之外,什么都没做。现在我们需要让这些函数发挥作用。
var svg = d3.select("svg#area")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
变量定义会svg获取svg具有指定 ID 的标签area,并添加一个组标签 `<group> g` 来定义 SVG 内的边距。所有绘制操作都将在该g标签内进行。
下一部分添加了一个path。这是数据和面积函数交汇的地方,也是整个示例的关键所在。D3使用每个数据点并将其传递给该area函数。该area函数将数据转换为 SVG 路径上的位置。结果如下:
<path class="area" d="M0,214.28571428571428L168.33333333333331,
171.42857142857142L336.66666666666663,0L505,128.57142857142858L505,
300L336.66666666666663,300L168.33333333333331,300L0,300Z"/>
最后两部分为SVG添加了坐标轴。关于这两部分没什么好说的。
把它弄漂亮
在“整合所有内容”部分,我忽略了.attr("class", "area")解释。D3 可以使用 `<div>` 标签添加任何属性attr()。我添加了一些class属性,以便可以设置图表的样式。SVG 使用的属性与标准 HTML 标签不同,但下面的样式使图表看起来简洁明了。
svg { border: 1px solid #dedede; }
.axis path, .axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.area { fill: #4ca3bd; }
本文最初发表于mattlayman.com。
文章来源:https://dev.to/mblayman/a-simple-d3-js-area-chart-4bkf
