zoukankan      html  css  js  c++  java
  • D3入门系列(2)--简单的条形图、折线图、散点图和文本换行

    SVG画布

    HTML 5 提供两种强有力的“画布”:SVG 和 Canvas。

    SVG的特点:

    • SVG 绘制的是矢量图,因此对图像进行放大不会失真
    • 基于 XML,可以为每个元素添加 JavaScript 事件处理器
    • 每个图形均视为对象,更改对象的属性,图形也会改变
    • 不适合游戏应用

    Canvas特点:

    • 绘制的是位图,图像放大后会失真
    • 不支持事件处理器
    • 能够以 .png 或 .jpg 格式保存图像
    • 适合游戏应用

    那么,对于数据可视化,SVG的优势就显而易见了,而且D3中很多图形生成器也是只支持SVG的

    注意,在 SVG 中,x 轴的正方向是水平向右,y 轴的正方向是垂直向下的

    Step1  添加SVG画布

     1  var width = 400;  //画布的宽度
     2  var height = 400;   //画布的高度
     3  
     4  var svg = d3.select("body")     //选择文档中的body元素
     5      .append("svg")          //添加一个svg元素
     6      .attr("width", width)       //设定宽度
     7      .attr("height", height);    //设定高度
     8 
     9 //画布周边的空白
    10  var padding = {left:30, right:30, top:20, bottom:20};

    如此,便可以在画布上作图了

    比例尺

    利用比例尺的目的主要是将某一区域的值映射到另一区域,其大小关系不变,也就是说,让图形自适应画布的大小。

    在数学中,x 的范围被称为定义域,y 的范围被称为值域。D3 中的比例尺,也有定义域和值域,分别被称为 domain 和 range。开发者需要指定 domain 和 range 的范围,如此即可得到一个计算关系。

    D3中的比例尺最常用的有两种:

    线性比例尺

    d3.scale.linear() 返回一个线性比例尺。domain() 和 range() 分别设定比例尺的定义域和值域

     1 var dataset = [1.2, 2.3, 0.9, 1.5, 3.3];
     2 
     3 var min = d3.min(dataset);
     4 var max = d3.max(dataset);
     5 
     6 var linear = d3.scale.linear()
     7         .domain([min, max])
     8         .range([0, 300]);
     9 
    10 linear(0.9);    //返回 0
    11 linear(2.3);    //返回 175
    12 linear(3.3);    //返回 300

    注意:d3.scale.linear() 的返回值,是可以当做函数来使用的。因此,才有这样的用法:linear(0.9)。

    序数比例尺

    d3.scale.ordinal() 返回一个线性比例尺。domain() 和 range() 分别设定比例尺的定义域和值域

     1 var index = [0, 1, 2, 3, 4];
     2 var color = ["red", "blue", "green", "yellow", "black"];
     3 
     4 var ordinal = d3.scale.ordinal()
     5         .domain(index)
     6         .range(color);
     7 
     8 ordinal(0); //返回 red
     9 ordinal(2); //返回 green
    10 ordinal(4); //返回 black

     0 对应颜色 red,1 对应 blue,依次类推

    Step2  定义数据和比例尺

     1  var dataset=[10, 20, 30, 40, 33, 24, 12, 5];
     2  
     3  //x轴
     4  var xScale=d3.scale.ordinal()
     5               .domain(d3.range(dataset.length))
     6               .rangeRoundBands([0,width-padding.left-padding.right]);
     7  
     8  //y轴
     9  var yScale=d3.scale.linear()
    10               .domain([0,d3.max(dataset)])
    11               .range([height-padding.top-padding.bottom,0]);//y轴正方向向下

    ordinal.rangeRoundBands - 用几个离散区间来分割一个连续的区间,区间边界和宽度会取整

     坐标轴

    D3中用于定义坐标轴的组件:d3.svg.axis()

     1 //数据
     2 var dataset = [ 2.5 , 2.1 , 1.7 , 1.3 , 0.9 ];
     3 //定义比例尺,其中使用了数组dataset
     4 var linear = d3.scale.linear()
     5       .domain([0, d3.max(dataset)])
     6       .range([0, 250]);
     7 //定义坐标轴,其中使用了线性比例尺linear
     8 var axis = d3.svg.axis()
     9      .scale(linear)      //指定比例尺
    10      .orient("bottom")   //指定刻度的方向
    11      .ticks(7);          //指定刻度的数量

     追加到画布上:

    1 svg.append("g")
    2   .attr("class","axis")
    3   .attr("transform","translate(20,130)")
    4   .call(axis);  

    注意: svg.append("g").call(axis); 与 axis(svg.append(g)); 是相等的。

    为axis设定样式(y也是常用的样式了)

    <style>
    .axis path,
    .axis line{
        fill: none;
        stroke: black;
        shape-rendering: crispEdges;
    }
    
    .axis text {
        font-family: sans-serif;
        font-size: 11px;
    }
    </style>

    Step3  定义坐标轴

    1 //定义x轴
    2 var xAxis = d3.svg.axis()
    3     .scale(xScale)
    4     .orient("bottom");
    5         
    6 //定义y轴
    7 var yAxis = d3.svg.axis()
    8     .scale(yScale)
    9     .orient("left");

    Step4  添加矩形和文字元素

    //矩形之间的空白
    var rectPadding = 4;
    
    //添加矩形元素
    var rects = svg.selectAll(".MyRect")
            .data(dataset)
            .enter()
            .append("rect")
            .attr("class","MyRect")
            .attr("transform","translate(" + padding.left + "," + padding.top + ")")
            .attr("x", function(d,i){
                return xScale(i) + rectPadding/2;
            } )
            .attr("y",function(d){
                return yScale(d);
            })
            .attr("width", xScale.rangeBand() - rectPadding )
            .attr("height", function(d){
                return height - padding.top - padding.bottom - yScale(d);
            })
    .attr("fill","steelblue");
    //添加文字元素 var texts = svg.selectAll(".MyText") .data(dataset) .enter() .append("text") .attr("class","MyText") .attr("transform","translate(" + padding.left + "," + padding.top + ")") .attr("x", function(d,i){ return xScale(i) + rectPadding/2; } ) .attr("y",function(d){ return yScale(d); }) .attr("dx",function(){ return (xScale.rangeBand() - rectPadding)/2; }) .attr("dy",function(d){ return 20; }) .text(function(d){ return d; });

    Step5  添加坐标轴元素

     1 //添加x轴
     2 svg.append("g")
     3   .attr("class","axis")
     4   .attr("transform","translate(" + padding.left + "," + (height - padding.bottom) + ")")
     5   .call(xAxis); 
     6         
     7 //添加y轴
     8 svg.append("g")
     9   .attr("class","axis")
    10   .attr("transform","translate(" + padding.left + "," + padding.top + ")")
    11   .call(yAxis);

    折线图

     1     //定义画布
     2     var width=400;
     3     var height=400;
     4 
     5     var svg=d3.select("body")
     6               .append("svg")
     7               .attr("width",width)
     8               .attr("height",height);
     9     //定义内边距
    10     var padding={left:20,right:20,top:10,bottom:10};
    11     
    12     //数据
    13     var dataset=[11,35,23,78,55,18,98,100,22,65]
    14     //定义比例尺
    15     var xscale=d3.scale.linear()
    16                  .domain([0,dataset.length-1])
    17                  .range([0,width-padding.left-padding.right])
    18     var yscale=d3.scale.linear()
    19                  .domain([0,d3.max(dataset)])
    20                  .range([height-padding.top-padding.bottom,0])
    21     //绘制坐标轴
    22     var xAxis=d3.svg.axis()
    23                 .scale(xscale)
    24                 .orient("bottom")
    25     var yAxis=d3.svg.axis()
    26                 .scale(yscale)
    27                 .orient("left")
    28     d3.select("svg")
    29      .append("g")
    30      .attr("transform","translate(" + padding.left + "," + (height - padding.bottom) + ")")
    31      .call(xAxis)
    32      .attr("class","axis")
    33      
    34     d3.select("svg")
    35      .append("g")
    36      .attr("transform","translate("+padding.left+","+padding.top+")")
    37      .call(yAxis)
    38      .attr("class","axis")
    39 
    40     
    41     //绘制图形
    42     var line_generator=d3.svg.line()
    43                          .x(function(d,i){
    44                              return xscale(i)//x轴的点用数据下标表示
    45                          })
    46                          .y(function(d){
    47                              return yscale(d)
    48                          });
    49                          //.interpolate("linear")
    50     var g=svg.append("g")
    51             .attr("transform","translate("+padding.left+","+padding.top+")")
    52 
    53     
    54     g.append("path")
    55       .attr("d",line_generator(dataset))
    56       .attr('stroke', 'black')
    57       .attr('stroke-width', 1)
    58       .attr("fill","none")

     散点图

    关键代码如下:

    var circle=svg.selectAll("circle")
                      .data(dataset)
                      .enter()
                      .append("circle")
                      .attr("fill","black")
                      .attr("r",3)
                      .attr("cx",function(d){
                          return padding.left+xscale(d[0])
                      })
                      .attr("cy",function(d){
                          return padding.top+yscale(d[1])  //重要!!
                      })

    上面标红的地方说明以下,本来想着y轴向下,便写成了 height-padding.bottom-yscale(d[1]) 

    但是发现画出的点的位置并不正确,原来原因是上面定义比例尺时,将值域已经设置成了 [height-2*padding.top,0] ,可以说,此时的坐标轴方向已经反转,所以此时的计算只需加上 padding.top 即可

    其实,更不易出错的方法是,将点放在一个group内,那么cx, cy只需按比例计算,然后在将group做一个transform变换即可。如上面画折线图的方法。

    文本的换行

    最后讲一下文本的换行

    方法1:利用tspan标签

    var str = "云中谁寄锦书来,雁字回时,月满西楼";            
                
    var text = svg.append("text")
                .attr("x",30)
                .attr("y",100)
                .attr("font-size",30)
                .attr("font-family","simsun");
    //将字符串分段
    var strs = str.split(",") ;
    
    text.selectAll("tspan")
        .data(strs)
        .enter()
        .append("tspan")
        .attr("x",text.attr("x"))  //文本从x=?处开始
        .attr("dy","1em")  //文本较y轴的相对位移,此处也就意味着换行
        .text(function(d){
            return d;
             });

    方法2:引用库 http://www.ourd3js.com/library/multext.js,其实质仍旧是tspan,只是进行了封装罢了

    文件里只实现了一个函数 appendMultiText(),其各参数的意义为:

    appendMultiText(
        container,            //文本的容器,可以是<svg>或<g> 
        str,                 //字符串
        posX,                 //文本的x坐标
        posY,                 //文本的y坐标
        width,                 //每一行的宽度,单位为像素
        fontsize,             //文字的大小(可省略),默认为 14
        fontfamily            //文字的字体(可省略),默认为 simsun, arial
    )

    小实例:

    var str = "青青子衿,悠悠我心,但为君故,沉吟至今。";            
                            
    var multext = appendMultiText(svg,str,30,100,120,20,"simsun");
                    
    multext.attr("transform","rotate(-20)");
  • 相关阅读:
    很漂亮的按钮css样式(没有用到图片,可直接拷贝代码使用)
    if、while中变量的作用域问题
    笔记
    搭建高可用mongodb集群(一)——配置mongodb
    Java编程:删除 List 元素的三种正确方法
    MySQL 数据类型
    MySQL 通用查询日志(General Query Log)
    mysql 创建一个用户,指定一个数据库
    MySQL 5.7 免安装版配置
    String,StringBuffer与StringBuilder的区别??
  • 原文地址:https://www.cnblogs.com/Hyacinth-Yuan/p/8059734.html
Copyright © 2011-2022 走看看