这里涉及了 HTML & CSS 的知识,比如说元素的样式和元素块,以后我会把相关知识补上。
绘制直线图
条形图实际上是矩形,而 HTML 的 div 元素是绘制矩形的最简单手段。(对于浏览器来说,HTML 中的一切元素都可以用来表示矩形)。
所以我们可以定义一个叫 bar 的 div 类,用于存放图表的公共属性。(除了高度,其他的属性应该是共享的)
div.bar { display: inline-block; 20px; height: 75px; /*最后这里会被覆写*/ margin-right: 2px; background-color: green;
-
关于类
元素的类作为 HTML 属性存在于标记代码中,同时 CSS 规则也可以引用它。除了为元素设定类以外,直接给元素应用样式也可以。(这里不太懂,下次遇到案例再写上)
D3 有一种方法用于快速添加或者删除元素的类:
.classed("bar",true) // 给选中的元素添加类 bar .classed("bar",false) //从元素总删除类 bar
-
关于样式
.style 方法用于直接为 HTML 元素应用 CSS 属性和值。这方法执行的结果等价于在 HTML 的 style 属性中直接写入 CSS 规则
<div style="height: 75px;"></div>
如果要生成条形图,每个条形图的高度必须是对应数据值得函数,D3 代码中可以对这个高度之进行重写:
.style("height", function(d) { var barHeight = d * 5; //这里是因为原始生成高度太矮了 return barHeight + "px"; });
-
关于属性设定
attr() 用于设定HTML 元素的属性和值。我们要给我们生成的 div 中添加 bar 类,需要这样写:
.attr("class","bar")
代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>D3: Our first bar chart with data</title> <script type="text/javascript" src="../d3.js"></script> <style type="text/css"> div.bar { display: inline-block; 20px; height: 75px; /* Gets overridden by D3-assigned height below */ margin-right: 2px; background-color: green; } </style> </head> <body> <script type="text/javascript"> var dataset = [ 25, 7, 5, 26, 11 ]; d3.select("body").selectAll("div") .data(dataset) .enter() .append("div") .attr("class", "bar") .style("height", function(d) { var barHeight = d * 5; return barHeight + "px"; });
随机数据
教材上面用了一个可以生成随机数值得方法,在这里记录一下:
这里是创建了一个名为 dataset 得空数组;
初始化一个循环25次的 for 循环,执行25次
每次生成一个介于0到30之间的随机数
把新数值追加到数组中(push() 是数组的方法,每次执行都会把一个新值推进数组末尾)。
var dataset = []; for( var i = 0; i < 25 ;i ++){ var newNumber= Math.random() * 30; dataset.push( newNumber);}
注意:
此时 push 进去的都是浮点数,可以用 Math.round() 或者Math.floor () 方法取整。前者是将数值想上取整,后者是向下取整。
var newNumber = Math.floor(Math.random() * 30);
dataset.push( newNumber();)
绘制 SVG
SVG 元素是通过标签中的属性 / 值对来指定SVG元素的个各方面特征,如:
<element property = "value"></element>
因为 SVG 元素存在于 DOM 中,与其他 HTML 元素一样,因此生成 SVG 图形依然要使用 append() 和 attr() 方法。
创建 SGV
首先要创建一个元素,以便在其中保存所有图形。这行代码先找到文档的 body 元素,然后再结束的 </body> 标签前添加一个新的 svg 元素。
d3.select("body").append("svg");
也可以使用一种更好的方式,把append() 返回的新元素保存在了变量 svg 中。有了这个引用,将来可以少些很多代码,从而不用总是写 da.select("svg") ,而是只要写 svg 即可:
var svg = d3.select("body").append("svg");
完整代码如下,DOM 中将创建一个新的空的 SVG元素。其中高度和宽度保存于变量中,可以方便引用。
var w =500; var h = 50; var svg = d3.select("body") .append("svg") .attr("width",w) .attr("height",h);
然使用data() 迭代每个数据节点,创建一个圆形。同时创建一个新的变量保存引用
var circles = svg.selectAll("circle") .data(dataset) .enter .append("circle");
创建位置和大小信息:
circle.attr( "cx", function(d, i)){ return (i*50) +25;
// 这里是通过引用所有的圆形的变量来设置每一个圆形的属性。(在SVG中,cx 是圆形圆心的 x 坐标,由于数据已经绑定到了圆形,所以对于每个圆形来说,都有其对应于原始数据的值。
// 并且其中的i值是自动生成的),同时,索引 i 是从function(d,i)中传入的,使其与 d 元素一致。
}) .attr("cry", h/2) .attr("r", function(d) { return d; });
// cy 是圆形圆心的 Y 的坐标,这里把 cy 设置成了 h 的一半。由于 h 保存着整个SVG元素的高度,所以这里是将所有圆形垂直居中
// 每个圆形的半径被设为 d,从而反映数据的大小
最后生成如图示:
也可以在上面添加色彩:
色彩填充( fill )和描边( stroke )同样也是属性,所以也可以通过attr()方法来设定。
.attr("fill", "yellow") .attr("stroke", "orange") .attr("stroke-width", function(d) { return d/2;}
最后图像如下:
源代码为:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>D3: Using color in SVG</title> <script type="text/javascript" src="../d3.js"></script> <style type="text/css"> /* No style rules here yet */ </style> </head> <body> <script type="text/javascript"> //Width and height var w = 500; var h = 100; //Data var dataset = [ 5, 10, 15, 20, 25 ]; //Create SVG element var svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); var circles = svg.selectAll("circle") .data(dataset) .enter() .append("circle"); circles.attr("cx", function(d, i) { return (i * 50) + 25; }) .attr("cy", h/2) .attr("r", function(d) { return d; }) .attr("fill", "yellow") .attr("stroke", "orange") .attr("stroke-width", function(d) { return d/2; }); </script> </body> </html>
关于绘制条形图的改进:
我们也可以通过 SVG 的方式来绘制条形图:
首先确定 SVG 的大小:
var w = 500; var h = 500;
然后让 D3 创建一个空元素,将其添加到 DOM 中.复习一下,这些代码会在结束的</body>标签前面插入新的 svg 元素,并将结果保存在了变量 svg 中,因此以后可以方便引用这个 sv g元素,而不用每次再使用 select() 之类的代码重新选择。
var svg = d3.select("body") .append("svg") .attr("width",w) .attr("height",h);
然后不创建 div,而是生成矩形元素 rect 并将其添加到 svg 中。
这段代码选择了 svg 中所有的矩形。但是,现在什么也没有,所以会返回一个空的元素集。
接下来 data(dataset) 看到了数据集中有20个值,就把这些值交给了 enter() 处理。每个rect 必须有 x, y width 和 height 属性。这里就是用 attr() 为每个新创建的 rect 设置了这些属性。但是这样会出现一个问题,就是所有的条形生成以后就重叠在了一起,而且此时并没有反映数据。
svg.selectAll("rect") .data(dataset) .enter() .append("rect") .attr("x",0) .attr("y",0) .attr("width",20) .attr("height",100);
为了解决重叠问题们同样需要使用 function() 函数:
.attr("x",function(d,i)) { return i*21; //由于矩形宽20像素,所以外加一个像素作为间距。 };
动态缩放:
当数据比较多的时候,最右边的矩形很有可能跑到 SVG 外面去,这时候就需要使用灵活、动态的坐标方案了。
首先,从改进设置每条矩阵 x 坐标的那行代码进行修改,这样的话,每个矩形就会进行缩放生成。当数据密的时候就会密集,当数据稀疏的时候间距就会拉大。
.attr("x",function(d,i) { return i * (w / dataset.length); })
以图为例:
当数据密集的时候:
当数据稀疏的时候:
这种解决方式并不好看。为了更加的美观,可以将矩形的宽度也成比例的缩放。
var w = 500; var h = 100; var barPadding = 1; //这里的变量是减去的间距的值
.attr("width",w / dataset.length - Padding)
然后再让数据值决定条形高度:
.attr("height",function(d)){ return d*4; // 放大四倍 });
结果如图所示:
由于 SVG 在绘制时,x 和 y 值指定的是它们左上角的坐标,所以 SVG 支持的只有左上角坐标系。如果我们需要改成一般的矩形图,就需要将每个图形的“下沿”与 SVG 的下沿对齐,每个 rect 的 height可以就设置为数据值的本身。
.attr("y", function(d){ return h - d; }) .attr("height",function(d){ return d; });
结果如图示:
上色
使用 fill 属性可以对其添加颜色:
.attr(" fill", "teal");
我们也可以让颜色反映数据的某些特性,对着条形图而言,这样做叫做双重编码,即同样的数据值可以被编码成俩种可以见的特性:条形高度和颜色。
.attr( "fill", function(d){ return "rgb( 0, 0, "+(d * 10) +" )";
附录:关于多值映射
D3 拥有多值映射机制,从而可以一次性设置多个值。而且依然是用 attr( ) 方法。假设要把一个圆形平移到 SVG 左上角,再设置成红色,可以每次单独调用 attr( ) :
svg.select("circlr") .attr("cx",0) .attr("cy",0) .attr( "fill", "red")
也可以把这三个属性的值都封装在一个对象中,然后统一交给 attr( ) :
svg.select("circlr") .attr({ cx: 0; cy: 0; fill: "red" });
源代码为:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>D3: Adding dynamic color, based on data</title> <script type="text/javascript" src="../d3.js"></script> <style type="text/css"> /* No style rules here yet */ </style> </head> <body> <script type="text/javascript"> //Width and height var w = 500; var h = 100; var barPadding = 1; var dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13, 11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ]; //Create SVG element var svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); svg.selectAll("rect") .data(dataset) .enter() .append("rect") .attr("x", function(d, i) { return i * (w / dataset.length); }) .attr("y", function(d) { return h - (d * 4); }) .attr("width", w / dataset.length - barPadding) .attr("height", function(d) { return d * 4; }) .attr("fill", function(d) { return "rgb(0, 0, " + Math.round(d * 10) + ")"; }); </script> </body> </html>