• D3.js系列——比例尺和坐标轴


      比例尺是 D3 中很重要的一个概念。绘制图形时直接用数值的大小来代表像素不是一种好方法,本章正是要解决此问题。

    一、为什么需要比例尺

      上一章制作了一个柱形图,当时有一个数组,绘图时,直接使用 250 给矩形的宽度赋值,即矩形的宽度就是 250 个像素。此方式非常具有局限性,如果数值过大或过小,例如:

    var dataset_1 = [ 2.5 , 2.1 , 1.7 , 1.3 , 0.9 ];
    var dataset_2 = [ 2500, 2100, 1700, 1300, 900 ];

      对以上两个数组,绝不可能用 2.5 个像素来代表矩形的宽度,那样根本看不见;也不可能用 2500 个像素来代表矩形的宽度,因为画布没有那么长。于是,我们需要一种计算关系,能够:将某一区域的值映射到另一区域,其大小关系不变这就是比例尺(Scale)。

    二、有哪些比例尺

      比例尺,很像数学中的函数。例如,对于一个一元二次函数,有 x 和 y 两个未知数,当 x 的值确定时,y 的值也就确定了。在数学中,x 的范围被称为定义域,y 的范围被称为值域

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

      D3 提供了多种比例尺,下面介绍最常用的两种。

    1、线性比例尺

      线性比例尺,能将一个连续的区间,映射到另一区间。要解决柱形图宽度的问题,就需要线性比例尺。

      假设有以下数组,现有要求如下:将 dataset 中最小的值,映射成 0;将最大的值,映射成 300。代码如下:

            var dataset = [1.2, 2.3, 0.9, 1.5, 3.3];
    
            var min = d3.min(dataset);
            var max = d3.max(dataset);
             
            var linear = d3.scale.linear()
                    .domain([min, max])//注意:domain()/range()里面是个数组形式哦
                    .range([0, 300]);
             
            linear(0.9);    //返回 0
            linear(2.3);    //返回 175
            linear(3.3);    //返回 300

      其中,d3.scale.linear() 返回一个线性比例尺。domain() 和 range() 分别设定比例尺的定义域和值域。在这里还用到了两个函数,它们经常与比例尺一起出现:

      d3.max() 、和d3.min():这两个函数能够求数组的最大值和最小值,是 D3 提供的。

      按照以上代码:比例尺的定义域 domain 为:[0.9, 3.3],比例尺的值域 range 为:[0, 300]

      因此,当输入 0.9 时,返回 0;当输入 3.3 时,返回 300。当输入 2.3 时呢?返回 175,这是按照线性函数的规则计算的。

      有一点请大家记住:d3.scale.linear() 的返回值,是可以当做函数来使用的。因此,才有这样的用法:linear(0.9)。

    2、序数比例尺

      有时候,定义域和值域不一定是连续的。例如,有两个数组:

    var index = [0, 1, 2, 3, 4];
    var color = ["red", "blue", "green", "yellow", "black"];

      我们希望 0 对应颜色 red,1 对应 blue,依次类推。但是,这些值都是离散的,线性比例尺不适合,需要用到序数比例尺。

    var ordinal = d3.scale.ordinal()
            .domain(index)
            .range(color);
     
    ordinal(0); //返回 red
    ordinal(2); //返回 green
    ordinal(4); //返回 black

      序数比例尺:d3.scale.ordinal();用法与线性比例尺是类似的。

     三、坐标轴由什么构成

       坐标轴,是可视化图表中经常出现的一种图形,由一些列线段和刻度组成。坐标轴在 SVG 中是没有现成的图形元素的,需要用其他的元素组合构成。D3 提供了坐标轴的组件,如此在 SVG 画布中绘制坐标轴变得像添加一个普通元素一样简单。

      在 SVG 画布的预定义元素里,有六种基本图形:

    • 矩形 <rect>
    • 圆形 <circle>
    • 椭圆 <ellipse>
    • 线段 <line>
    • 折线 <polyline>
    • 多边形 <polygon>

      另外,还有一种比较特殊,也是功能最强的元素:

    • 路径 <path>

      画布中的所有图形,都是由以上七种元素组成。

      显然,这里面没有坐标轴 <axis> 这种元素。如果有的话,我们可以采用类似以下的方式定义:<axis x1="" x2="" ...></axis>,很可惜,没有这种元素。但是,这种设计是合理的:不可能为每一种图形都配备一个单独的元素,那样 SVG 就会过于庞大。

      因此,我们需要用其他元素来组合成坐标轴,最终使其变为类似以下的形式:

    <g>
    <!-- 第一个刻度 -->
    <g>
    <line></line>   <!-- 第一个刻度的直线 -->
    <text></text>   <!-- 第一个刻度的文字 -->
    </g>
    <!-- 第二个刻度 -->
    <g>
    <line></line>   <!-- 第二个刻度的直线 -->
    <text></text>   <!-- 第二个刻度的文字 -->
    </g> 
    ...
    <!-- 坐标轴的轴线 -->
    <path></path>
    </g>

      分组元素 <g>,是 SVG 画布中的元素,意思是 group。此元素是将其他元素进行组合的容器,在这里是用于将坐标轴的其他元素分组存放。

      如果需要手动添加这些元素就太麻烦了,为此,D3 提供了一个组件:d3.svg.axis()。它为我们完成了以上工作。

    四、定义坐标轴

      上一章提到了比例尺的概念,要生成坐标轴,需要用到比例尺,它们二者经常是一起使用的。下面,在上一章的数据和比例尺的基础上,添加一个坐标轴的组件。

    //数据
    var dataset = [ 2.5 , 2.1 , 1.7 , 1.3 , 0.9 ];
    //定义比例尺
    var linear = d3.scale.linear()
          .domain([0, d3.max(dataset)])
          .range([0, 250]);
     
    var axis = d3.svg.axis()
         .scale(linear)      //指定比例尺
         .orient("bottom")   //指定刻度的方向
         .ticks(7);          //指定刻度的数量

      主要看下定义坐标轴的方法,其中使用了线性比例尺 linear。其中:

      d3.svg.axis():D3 中坐标轴的组件,能够在 SVG 中生成组成坐标轴的元素。

      scale():指定比例尺。

      orient():指定刻度的朝向,bottom 表示在坐标轴的下方显示。

      ticks():指定刻度的数量。

    五、在 SVG 中添加坐标轴

      定义了坐标轴之后,只需要在 SVG 中添加一个分组元素 <g>,再将坐标轴的其他元素添加到这个 <g> 里即可。代码如下:

    svg.append("g")
       .call(axis);

      上面有一个 call() 函数,其参数是前面定义的坐标轴 axis。在 D3 中,call() 的参数是一个函数。调用之后,将当前的选择集作为参数传递给此函数。也就是说,以下两段代码是相等的。

    function foo(selection) {
        selection
            .attr("name1", "value1")
            .attr("name2", "value2");
    }
    foo(d3.selectAll("div"));
    //等价于
    d3.selectAll("div").call(foo);
    
    svg.append("g").call(axis);
    //等价于
    axis(svg.append(g));

    六、设定坐标轴的样式和位置

      默认的坐标轴样式不太美观,下面提供一个常见的样式:

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

      分别定义了类 axis 下的 path、line、text 元素的样式。接下来,只需要将坐标轴的类设定为 axis 即可。

      坐标轴的位置,可以通过 transform 属性来设定。通常在添加元素的时候就一并设定,写成如下形式:

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

    七、应用

    <html> 
    <head> 
        <meta charset="utf-8"> 
        <title>比例尺和坐标轴</title> 
        <style>
        .axis path,
        .axis line{
            fill: none;
            stroke: black;
            shape-rendering: crispEdges;
        }
         
        .axis text {
            font-family: sans-serif;
            font-size: 11px;
        }
        </style>
    </head> 
    <body>
        <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> 
        <script>
        var width = 300;  //画布的宽度
        var height = 300;   //画布的高度
     
        var svg = d3.select("body")     //选择文档中的body元素
            .append("svg")          //添加一个svg元素
            .attr("width", width)       //设定宽度
            .attr("height", height);    //设定高度
    
        var dataset = [ 2.5 , 2.1 , 1.8 , 1.3 , 0.9 ]; //数据(表示矩形的宽度)
        var linear = d3.scale.linear()
                    .domain([0,d3.max(dataset)])
                    .range([0,250]);
    
        var rectHeight = 25;   //每个矩形所占的像素高度(包括空白)
     
        svg.selectAll("rect")
            .data(dataset)
            .enter()
            .append("rect")
            .attr("x",20)
            .attr("y",function(d,i){
                 return i * rectHeight;
            })
            .attr("width",function(d){
                 return linear(d);  // 此处应用比例尺
            })
            .attr("height",rectHeight-2)
            .attr("fill","steelblue");
    
        //添加坐标轴
        var axis = d3.svg.axis()
            .scale(linear)
            .orient("bottom")
            .ticks(7);
        svg.append("g")
            .attr("class","axis")
            .attr("transform","translate(20,130)")
            .call(axis);
        </script> 
    </body> 
    </html>
  • 相关阅读:
    104_如何彻底删除VMware
    学习笔记(25)- NLP的几个概念
    学习笔记(24)- plato-训练中文模型
    学习笔记(23)- plato-准备中文语料
    学习笔记(22)- plato-训练端到端的模型
    学习笔记(21)- texar 文本生成
    学习笔记(35)-安装pyhanlp
    NLP直播-1 词向量与ELMo模型
    线上学习-语言模型 language model
    学习笔记(20)- Google LaserTagger
  • 原文地址:https://www.cnblogs.com/goloving/p/8609351.html
Copyright © 2020-2023  润新知