我的目标是,注释100个d3.js的例子。
可能是史上最详细的 。
Area Chart
是Basic Charts
里的第一个例子。
解析
1
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
纯粹的JavaScript代码。
首先定义上下左右margin
的值。然后以960和500作为整个图表(包括空白部分)的宽和高,除去空白部分,算出图表真实的大小。这也就是x轴(width
)和y轴(height
)的长度。
2
var parseDate = d3.time.format("%d-%b-%y").parse;
使用了d3.time
的Time Formatting。它用来在时间对象和字符串之间相互转换。%d-%b-%y
叫做specifier
,表示的是“24-Mar-08”这种时间样式。parse
是一个函数,赋值给parseDate
变量,方便以后的调用。
整行代码的意思就是,把的一个函数赋给了parseDate
,这个函数可以把长得像“24-Mar-08”的字符串转换成时间对象。
3
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
这两句都是和d3.scale
相关的,所以放在一起讲。先提出一个问题来解释一下scale
是做什么的。
如何从 0-100 映射到 0-1 ? 答案自然就是
0 -> 0
1 -> 0.01
2 -> 0.02
...
以此类推。那从 100-0 映射到 0-1 呢? 从 1-12 到 1月到12月 呢? 或者从 0 - 1 判断 男女呢? scale
就是做这个用的,它主要有三类:
数字 (0-1)
文字 (男女)
时间 (月份)
而刚刚的例子中,0-100 叫做domain
,可以理解为输入的值域。0-1 叫做range
,就是输出的值域。
x
这个scale
就是时间,它输出的值域会在0
到width
之间。y
这个scale
就是数字,它输出的值域会在height
到0
之间。注意,这里并没有设置domain
,也就是输入的值域。因为,输入值域当然要等到读取数据之后才会明了。
所以,从这里看出,x
和y
的输出值域与width
和height
有密切的关系。理所当然的,它们之后一定会被用作构图。
4
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
这两句看字面意义就可以了。它们和d3.svg.axis
相关,是在建立坐标轴。xAxis
使用了上一段创建的scale
x
,同时指定了它的朝向是向下。意思是,水平的横轴,文字在轴的下面。以此类推,yAxis
使用了scale
y
,垂直的纵轴,文字在轴的左边。
5
var area = d3.svg.area()
.x(function(d) { return x(d.date); })
.y0(height)
.y1(function(d) { return y(d.close); });
建立了一个svg
的area
。区域 (area
) 的意思是,对于一个x
,y0
和y1
之间的部分表示此x覆盖的区域。
x
和y1
都接受了一个函数,函数的形参是d
。当渲染的时候,d3.js
会把每一条数据都一一传进来。
为什么y0
是height
,而y1
是d.close
?
对于某个的x
,y0
表示覆盖区域垂直方向的起始点,y1
表示终止点。从d3.js
的角度来说,左上角为(0, 0),对这个点将会绘制(x
, y0
=height
)到(x, y1
=d.close
),也就是从图表的最底部往上画,画到d.close
。
6
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
这部分比较简单,找到html
里的body
,在其末尾加上一个svg
,并且指定了svg
的高度的宽度。最后在svg
里加入一个g
(group),并且留出margin的距离。
7
d3.tsv("data.tsv", function (error, data) {
if (error) throw error;
读取数据的函数,数据源是tsv
(tab separated values)。当d3.js
读取并且处理完数据,会执行这个function(error, data)
这个callback。而数据会以数组的形式存放在形参data
里面.
8
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
既然data
是数组,那我们就对它的每一个元素进行处理。
对date
使用之前保存下来的parseDate
函数,记不记得,它能够处理类似24-Mar-08
的字符串。
而close
仅仅是从字符串换成数字。
9
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.close; })]);
回忆一下,刚刚的x
和y
作为scale
只设定了与画图面积相关的range
(输出值域)。现在读完数据了,需要设置domain
(输入的值域)。d3.extent
是一个数组的辅助函数,可以返回其最小值和最大值。
完成以后,x
scale
的映射是,[时间最小值,时间最大值] -> [0, width],也就是xAxis的最左和最右。y
scale
的映射是,[0,最大值] -> [height, 0],也就是yAxis的最低和最高。
10
最后几行比较无趣,一起说吧。
svg.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
用处理好的数据建立area
。唯一要说一说的是这个datum
,它一般用于静态的数据可视化。也就是说,如果这个元素不需要随着数据的变化而变化,那么用datum
是合适的。更加具体的解释,或者是动态更新数据可视化的方法,参见这里。
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
建立x轴的g
——— g
是group
的意思。除了建立的时候用了之前的xAxis
,其他几行无非是加一些css class,然后下移到(0, height)位置,也就是图表的下方。
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Price ($)");
建立y轴的group
,同时加一个文字,转90度,设定初始位置和偏移量,同时改变字体本身锚的位置(一般都以左上角为锚)。
参考:
1 https://github.com/mbostock/d3/wiki/Gallery#basic-charts
2 http://bl.ocks.org/mbostock/3883195
3 https://gist.github.com/hugolpz/824446bb2f9bc8cce607
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。