D3.js 基于数据的绘图

 

 

 

 

这里涉及了 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 规则

<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;
                width: 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>

 

posted on 2018-11-03 23:44  雪原那么远  阅读(2524)  评论(0编辑  收藏  举报

导航