这里涉及了 HTML & CSS 的知识,比如说元素的样式和元素块,以后我会把相关知识补上。
绘制直线图
条形图实际上是矩形,而 HTML 的 div 元素是绘制矩形的最简单手段。(对于浏览器来说,HTML 中的一切元素都可以用来表示矩形)。
所以我们可以定义一个叫 bar 的 div 类,用于存放图表的公共属性。(除了高度,其他的属性应该是共享的)
div.bar {
display: inline-block; width: 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 规则
如果要生成条形图,每个条形图的高度必须是对应数据值得函数,D3 代码中可以对这个高度之进行重写:
.style("height", function(d) {var barHeight = d * 5; //这里是因为原始生成高度太矮了
return barHeight + "px";
});
关于属性设定
attr() 用于设定HTML 元素的属性和值。我们要给我们生成的 div 中添加 bar 类,需要这样写:
.attr("class","bar")
代码如下:
D3: Our first bar chart with datadisplay: inline-block;
width: 20px;
height: 75px;/*Gets overridden by D3-assigned height below*/margin-right: 2px;
background-color: green;
}
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元素的个各方面特征,如:
因为 SVG 元素存在于 DOM 中,与其他 HTML 元素一样,因此生成 SVG 图形依然要使用 append() 和 attr() 方法。
创建 SGV
首先要创建一个元素,以便在其中保存所有图形。这行代码先找到文档的 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) {returnd; });
// cy 是圆形圆心的 Y 的坐标,这里把 cy 设置成了 h 的一半。由于 h 保存着整个SVG元素的高度,所以这里是将所有圆形垂直居中
// 每个圆形的半径被设为 d,从而反映数据的大小
最后生成如图示:
也可以在上面添加色彩:
色彩填充( fill )和描边( stroke )同样也是属性,所以也可以通过attr()方法来设定。
.attr("fill", "yellow")
.attr("stroke", "orange")
.attr("stroke-width", function(d) {return d/2;}
最后图像如下:
源代码为:
D3: Using color in SVG/*No style rules here yet*/
//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) {returnd;
})
.attr("fill", "yellow")
.attr("stroke", "orange")
.attr("stroke-width", function(d) {return d/2;
});
关于绘制条形图的改进:
我们也可以通过 SVG 的方式来绘制条形图:
首先确定 SVG 的大小:
var w = 500;var h = 500;
然后让 D3 创建一个空元素,将其添加到 DOM 中.复习一下,这些代码会在结束的标签前面插入新的 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){returnd;
});
结果如图示:
上色
使用 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"});
源代码为:
D3: Adding dynamic color, based on data/*No style rules here yet*/
//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) + ")";
});