D3.js

作者: 强某某 | 来源:发表于2020-05-15 18:57 被阅读0次

    D3.js

    为什么学习D3

    D3.js和threejs的应用场景完全不一样。threejs主要应用与基于webGL的3D场景,而D3.js确主要应用与2D场景。

    它们一起形成了一种互补关系。

    简而言之D3JS就是一个数据可视化的库。

    那什么是数据可视化呢?

    给出一组数据 [10,80,40,100,30,20,50]

    image-20190801162422591.png

    类似的库 eharts

    ECharts,一个使用 JavaScript 实现的开源可视化库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器,ECharts 提供了常规的折线图柱状图散点图饼图 等等。

    和eharts的区别

    eharts是封装好的各种的图表可以直接拿来使用,类似于图表模具,直接拿来使用即可。

    D3.js就像画笔一样,一切都由你自由发挥。

    基本介绍

    D3.jsData-Driven Documents)是一个使用动态图形进行数据可视化JavaScript程序库。与W3C标准兼容,并且利用广泛实现的SVG、JavaScript和CSS标准,改良自早期的Protovis程序库。与其他的程序库相比,D3对视图结果有很大的可控性。D3是2011年面世的,同年的8月发布了2.0.0版。到2018年4月,D3已被更新到了5.5.0版[1]

    发展历史

    在D3.js开发之前已经有出现过许多尝试做数据可视化的包,例如Prefuse,Flare和Protovis程序库,他们都可以视为D3.js的前身。然而Prefuse和Flare皆有缺点,皆不能只透过浏览器完成渲染,皆须要透过额外插件来完成。

    例如2005年发布的Prefuse是一个数据可视化程序库,但是它需要透过网页的Java插件才能于浏览器中呈现;而Flare是2007年发布的另一个数据可视化工具包,由于其是使用ActionScript编程语言开发,因此也需要额外插件,即Flash插件才能完成渲染。

    2009年,史丹佛大学的史丹佛可视化团队(Stanford Visualization Group)的杰佛瑞·赫尔、迈克·保斯托和瓦迪姆·欧格菲兹齐利用开发Prefuse和Flare的经验开始用Javscript开发了可从给定数据产生SVG图形的Protovis程序库。而Protovis程序库在业界和学界皆有一定的知名度[3]

    2011年,史丹佛可视化团队停止开发Protovis,并开始开发新的数据可视化程序库,借由之前开发Protovis的经验,开发出了D3.js程序库,在注重于Web标准的同时提供了更丰富的平台也有了更好的性能[4]

    技术原理

    D3.js透过预先创建好迁入于网页中的JavaScript函数来选择网页元素、创建SVG元素、调整CSS来呈现数据,并且也可以设置动画、动态改变对象状态或加入工具提示来完成用户交互功能。使用简单的D3.js函数就能够将大型的数据数据结构与SVG对象进行绑定,并且能生成格式化文本和各种图表。

    基本使用

    hello world

    先尝试用 D3 写第一个 HelloWorld 程序。学编程入门的第一个程序都是在屏幕上输出 HelloWorld,本课稍微有些不同,不是单纯的输出。

    在 HTML 中输出 HelloWorld 是怎样的呢,先看下面的代码。

    <html> 
      <head> 
            <meta charset="utf-8"> 
            <title>HelloWorld</title> 
      </head> 
        <body> 
            <p>Hello World 1</p>
            <p>Hello World 2</p>
        </body> 
    </html>
    
    用 JavaScript 来更改 HelloWorld

    对于上面输出的内容,如果想用 JavaScript 来更改这两行文字,怎么办呢?我们会添加代码变为:

    <html> 
      <head> 
            <meta charset="utf-8"> 
            <title>HelloWorld</title> 
      </head> 
        <body> 
        <p>Hello World 1</p>
        <p>Hello World 2</p>
            <script>
            var paragraphs = document.getElementsByTagName("p");
            for (var i = 0; i < paragraphs.length; i++) {
              var paragraph = paragraphs.item(i);
              paragraph.innerHTML = "I like dog.";
            }          
            </script> 
        </body> 
    </html>
    
    用 D3 来更改 HelloWorld

    如果使用 D3.js 来修改这两行呢?只需添加一行代码即可。注意不要忘了引用 D3.js 源文件。

    引入

    <script src="https://d3js.org/d3.v5.js"></script>
    
    <html> 
      <head> 
            <meta charset="utf-8"> 
            <title>HelloWorld</title> 
      </head> 
        <body> 
            <p>Hello World 1</p>
            <p>Hello World 2</p>
            <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> 
            <script>  
            d3.select("body").selectAll("p").text("www.ourd3js.com");      
            </script> 
        </body> 
    </html>
    

    接下来改变字体的颜色和大小,稍微修改上述代码:

    //选择<body>中所有的<p>,其文本内容为 www.ourd3js.com,选择集保存在变量 p 中
    var p = d3.select("body")
              .selectAll("p")
              .text("www.ourd3js.com");
    
    //修改段落的颜色和字体大小
    p.style("color","red")
     .style("font-size","72px");
    

    选择元素

    在 D3 中,用于选择元素的函数有两个:

    • d3.select():是选择所有指定元素的第一个
    • d3.selectAll():是选择指定元素的全部

    这两个函数返回的结果称为选择集。

    例如,选择集的常见用法如下。

    var body = d3.select("body"); //选择文档中的body元素
    var p1 = body.select("p");      //选择body中的第一个p元素
    var p = body.selectAll("p");    //选择body中的所有p元素
    var svg = body.select("svg");   //选择body中的svg元素
    var rects = svg.selectAll("rect");  //选择svg中所有的svg元素
    

    绑定数据

    选择集和绑定数据通常是一起使用的。

    D3 有一个很独特的功能:能将数据绑定到 DOM 上,也就是绑定到文档上。这么说可能不好理解,例如网页中有段落元素 p 和一个整数 5,于是可以将整数 5 与 p 绑定到一起。绑定之后,当需要依靠这个数据才操作元素的时候,会很方便。

    D3 中是通过以下两个函数来绑定数据的:

    • datum():绑定一个数据到选择集上
    • data():绑定一个数组到选择集上,数组的各项值分别与选择集的各元素绑定

    相对而言,data() 比较常用。

    假设现在有三个段落元素如下。

    <p>Apple</p>
    <p>Pear</p>
    <p>Banana</p>
    

    datum()

    假设有一字符串 China,要将此字符串分别与三个段落元素绑定,代码如下:

    var str = "China";
    
    var body = d3.select("body");
    var p = body.selectAll("p");
    
    p.datum(str);
    
    p.text(function(d, i){
        return "第 "+ i + " 个元素绑定的数据是 " + d;
    });
    

    绑定数据后,使用此数据来修改三个段落元素的内容,其结果如下:

    第 0 个元素绑定的数据是 China
    
    第 1 个元素绑定的数据是 China
    
    第 2 个元素绑定的数据是 China
    

    在上面的代码中,用到了一个无名函数 function(d, i)。当选择集需要使用被绑定的数据时,常需要这么使用。其包含两个参数,其中:

    • d 代表数据,也就是与某元素绑定的数据。
    • i 代表索引,代表数据的索引号,从 0 开始。

    例如,上述例子中:第 0 个元素 apple 绑定的数据是 China。

    data()

    有一个数组,接下来要分别将数组的各元素绑定到三个段落元素上。

    var dataset = ["I like dog","I like cat","I like snake"];
    

    绑定之后,其对应关系的要求为:

    • Apple 与 I like dog 绑定
    • Pear 与 I like cat 绑定
    • Banana 与 I like snake 绑定

    调用 data() 绑定数据,并替换三个段落元素的字符串为被绑定的字符串,代码如下:

    var body = d3.select("body");
    var p = body.selectAll("p");
    
    p.data(dataset)
      .text(function(d, i){
          return d;
    });
    

    这段代码也用到了一个无名函数 function(d, i),其对应的情况如下:

    • 当 i == 0 时, d 为 I like dog。
    • 当 i == 1 时, d 为 I like cat。
    • 当 i == 2 时, d 为 I like snake。

    此时,三个段落元素与数组 dataset 的三个字符串是一一对应的,因此,在函数 function(d, i) 直接 return d 即可。

    结果自然是三个段落的文字分别变成了数组的三个字符串。

    I like dog
    
    I like cat
    
    I like snake
    

    选择、插入、删除元素

    已经讲解了 select 和 selectAll,以及选择集的概念。本节具体讲解这两个函数的用法。

    假设在 body 中有三个段落元素:

    <p>Apple</p>
    <p>Pear</p>
    <p>Banana</p>
    

    现在,要分别完成以下四种选择元素的任务。

    选择第一个 p 元素
    t("p");
    p1.style("color","red");
    
    选择三个 p 元素
    var p = body.selectAll("p");
    p.style("color","red");
    
    选择第二个 p 元素

    有不少方法,一种比较简单的是给第二个元素添加一个 id 号。

    Pear

    然后,使用 select 选择元素,注意参数中 id 名称前要加 # 号。

    var p2 = body.select("#myid");
    p2.style("color","red");
    
    选择后两个 p 元素

    给后两个元素添加 class,

    <p class="myclass">Pear</p>
    <p class="myclass">Banana</p>
    

    由于需要选择多个元素,要用 selectAll。注意参数,class 名称前要加一个点。

    var p = body.selectAll(".myclass");
    p.style("color","red");
    

    插入元素

    插入元素涉及的函数有两个:

    • append():在选择集末尾插入元素
    • insert():在选择集前面插入元素

    假设有三个段落元素,与上文相同。

    append()

    body.append("p")
        .text("append p element");
    

    在 body 的末尾添加一个 p 元素,结果为:

    Apple
    Pear
    Banana
    append p element
    

    insert()

    在 body 中 id 为 myid 的元素前添加一个段落元素。

    body.insert("p","#myid")
      .text("insert p element");
    

    已经指定了 Pear 段落的 id 为 myid,因此结果如下。

    Apple
    insert p element
    Pear
    Banana
    

    删除元素

    删除一个元素时,对于选择的元素,使用 remove 即可,例如:

    var p = body.select("#myid");
    p.remove();
    

    SVG 基本使用

    SVG 意为可缩放矢量图形(Scalable Vector Graphics)。

    SVG 使用 XML 格式定义图像。

    什么是svg

    • SVG 指可伸缩矢量图形 (Scalable Vector Graphics)
    • SVG 用来定义用于网络的基于矢量的图形
    • SVG 使用 XML 格式定义图形
    • SVG 图像在放大或改变尺寸的情况下其图形质量不会有所损失
    • SVG 是万维网联盟的标准
    • SVG 与诸如 DOM 和 XSL 之类的 W3C 标准是一个整体

    hello-world

    <html>
    <body>
     
    <h1>My first SVG</h1>
     
    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
      <circle cx="100" cy="50" r="40" stroke="black"
      stroke-width="2" fill="red" />
    </svg>
     
    </body>
    </html>
    

    简单的 SVG 实例

    一个简单的SVG图形例子:

    这里是SVG文件(SVG文件的保存与SVG扩展):

    <?xml version="1.0" standalone="no"?>
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
    
    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
      <circle cx="100" cy="50" r="40" stroke="black"
      stroke-width="2" fill="red" />
    </svg>
    

    第一行包含了 XML 声明。请注意 standalone 属性!该属性规定此 SVG 文件是否是"独立的",或含有对外部文件的引用。

    standalone="no" 意味着 SVG 文档会引用一个外部文件 - 在这里,是 DTD 文件。

    第二和第三行引用了这个外部的 SVG DTD。该 DTD 位于 "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"。该 DTD 位于 W3C,含有所有允许的 SVG 元素。

    SVG 代码以 <svg>元素开始,包括开启标签<svg>和关闭标签 </svg>。这是根元素。width 和 height 属性可设置此 SVG 文档的宽度和高度。version 属性可定义所使用的 SVG 版本,xmlns 属性可定义 SVG 命名空间。

    SVG 的<circle> 用来创建一个圆。cx 和 cy 属性定义圆中心的 x 和 y 坐标。如果忽略这两个属性,那么圆点会被设置为 (0, 0)。r 属性定义圆的半径。

    stroke 和 stroke-width 属性控制如何显示形状的轮廓。我们把圆的轮廓设置为 2px 宽,黑边框。

    fill 属性设置形状内的颜色。我们把填充颜色设置为红色。

    关闭标签的作用是关闭 SVG 元素和文档本身。

    SVG 在 HTML 页面

    SVG 文件可通过以下标签嵌入 HTML 文档:<embed>、<object> 或者 <iframe>。

    SVG的代码可以直接嵌入到HTML页面中,或您可以直接链接到SVG文件。

    使用 <embed> 标签
    <embed src="circle1.svg" type="image/svg+xml" />
    
    直接在HTML嵌入SVG代码

    在Firefox、Internet Explorer9、谷歌Chrome和Safari中,你可以直接在HTML嵌入SVG代码。

    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
       <circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red" />
    </svg>
    

    SVG图形

    SVG有一些预定义的形状元素,可被开发者使用和操作:

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

    EX1:

    <rect> 标签可用来创建矩形,以及矩形的变种:

    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
      <rect width="300" height="100"
      style="fill:rgb(0,0,255);stroke-width:1;stroke:rgb(0,0,0)"/>
    </svg>
    
    • rect 元素的 width 和 height 属性可定义矩形的高度和宽度
    • style 属性用来定义 CSS 属性
    • CSS 的 fill 属性定义矩形的填充颜色(rgb 值、颜色名或者十六进制值)
    • CSS 的 stroke-width 属性定义矩形边框的宽度
    • CSS 的 stroke 属性定义矩形边框的颜色

    EX2:

    让我们看看另一个例子,它包含一些新的属性:

    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
      <rect x="50" y="20" width="150" height="150"
      style="fill:blue;stroke:pink;stroke-width:5;fill-opacity:0.1;
      stroke-opacity:0.9"/>
    </svg>
    
    • x 属性定义矩形的左侧位置(例如,x="0" 定义矩形到浏览器窗口左侧的距离是 0px)
    • y 属性定义矩形的顶端位置(例如,y="0" 定义矩形到浏览器窗口顶端的距离是 0px)
    • CSS 的 fill-opacity 属性定义填充颜色透明度(合法的范围是:0 - 1)
    • CSS 的 stroke-opacity 属性定义轮廓颜色的透明度(合法的范围是:0 - 1)
    圆形

    <circle> 标签可用来创建一个圆:

    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
      <circle cx="100" cy="50" r="40" stroke="black"
      stroke-width="2" fill="red"/>
    </svg>
    
    • cx和cy属性定义圆点的x和y坐标。如果省略cx和cy,圆的中心会被设置为(0, 0)
    • r属性定义圆的半径
    椭圆

    <ellipse> 元素是用来创建一个椭圆:

    椭圆与圆很相似。不同之处在于椭圆有不同的x和y半径,而圆的x和y半径是相同的:

    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
      <ellipse cx="300" cy="80" rx="100" ry="50"
      style="fill:yellow;stroke:purple;stroke-width:2"/>
    </svg>
    
    直线

    <line> 元素是用来创建一个直线:

    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
      <line x1="0" y1="0" x2="200" y2="200"
      style="stroke:rgb(255,0,0);stroke-width:2"/>
    </svg>
    
    • x1 属性在 x 轴定义线条的开始
    • y1 属性在 y 轴定义线条的开始
    • x2 属性在 x 轴定义线条的结束
    • y2 属性在 y 轴定义线条的结束
    多边形

    <polygon> 标签用来创建含有不少于三个边的图形。

    polygon来自希腊。 "Poly" 意味 "many" , "gon" 意味 "angle".

    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
      <polygon points="200,10 250,190 160,210"
      style="fill:lime;stroke:purple;stroke-width:1"/>
    </svg>
    

    下面的示例创建一个四边的多边形:

    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
      <polygon points="220,10 300,210 170,250 123,234"
      style="fill:lime;stroke:purple;stroke-width:1"/>
    </svg>
    
    曲线

    <polyline> 元素是用于创建任何只有直线的形状:

    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
      <polyline points="20,20 40,25 60,40 80,120 120,140 200,180"
      style="fill:none;stroke:black;stroke-width:3" />
    </svg>
    

    只有直线的另一个例子:

    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
      <polyline points="0,40 40,40 40,80 80,80 80,120 120,120 120,160" style="fill:white;stroke:red;stroke-width:4" />
    </svg>
    
    路径

    <path> 元素用于定义一个路径。

    下面的命令可用于路径数据:

    • M = moveto
    • L = lineto
    • H = horizontal lineto
    • V = vertical lineto
    • C = curveto
    • S = smooth curveto
    • Q = quadratic Bézier curve
    • T = smooth quadratic Bézier curveto
    • A = elliptical Arc
    • Z = closepath

    例子定义了一条路径,它开始于位置150 0,到达位置75 200,然后从那里开始到225 200,最后在150 0关闭路径。

    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
        <path d="M150 0 L75 200 L225 200 Z" />
    </svg>
    
    文本

    <text> 元素用于定义文本。

    EX:1

    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
      <text x="0" y="15" fill="red">I love SVG</text>
    </svg>
    
    Svg与Canvas的区别?
    1. 绘制的图片格式不同
    Canvas 的工具getContext 绘制出来的图形或传入的图片都依赖分辨率,能够以 .png 和 .jpg格式保存存储图像,可以说是位图
    
    SVG 可以在H5中直接绘制,但绘制的是矢量图
    
    由于位图依赖分辨率,矢量图不依赖分辨率,所以Canvas和SVG的图片格式的不同实际上是他们绘制出来的图片的格式不同造成的。
    
    1. Canvas不支持事件处理器,SVG支持事件处理器
    Canvas 绘制的图像 都在Canvas这个画布里面,是Canvas的一部分,不能用js获取已经绘制好的图形元素。
    
    1. 适用范围不同
    Canvas是逐像素进行渲染的,一旦图形绘制完成,就不会继续被浏览器关注。而SVG是通过DOM操作来显示的。
    
    所以Canvas的文本渲染能力弱,而SVG最适合带有大型渲染区域的应用程序,比如地图。
    
    而Canvas 最适合有许多对象要被频繁重绘的图形密集型游戏。
    
    而SVG由于DOM操作 在复杂度高的游戏应用中 会减慢渲染速度。所以不适合在游戏应用。
    

    实践

    做一个简单的柱状图

    image-20190802172953054.png
    画布

    前几章的处理对象都是 HTML 的文字,没有涉及图形的制作。

    要绘图,首要需要的是一块绘图的“画布”。

    HTML 5 提供两种强有力的“画布”:SVGCanvas

    添加画布

    D3 虽然没有明文规定一定要在 SVG 中绘图,但是 D3 提供了众多的 SVG 图形的生成器,它们都是只支持 SVG 的。因此,建议使用 SVG 画布。

    使用 D3 在 body 元素中添加 svg 的代码如下。

    var width = 300;  //画布的宽度
    var height = 300;   //画布的高度
    
    var svg = d3.select("body")     //选择文档中的body元素
        .append("svg")          //添加一个svg元素
        .attr("width", width)       //设定宽度
        .attr("height", height);    //设定高度
    

    有了画布,接下来就可以在画布上作图了。

    绘制矩形

    本文绘制一个横向的柱形图。只绘制矩形,不绘制文字和坐标轴。

    在 SVG 中,矩形的元素标签是 rect。例如:

    <svg>
      <rect></rect>
      <rect></rect>
    </svg>
    

    上面的 rect 里没有矩形的属性。矩形的属性,常用的有四个:

    • x:矩形左上角的 x 坐标
    • y:矩形左上角的 y 坐标
    • width:矩形的宽度
    • height:矩形的高度

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

    现在给出一组数据,要对此进行可视化。数据如下:

    var dataset = [ 250 , 210 , 170 , 130 , 90 ];  //数据(表示矩形的宽度)
    

    为简单起见,我们直接用数值的大小来表示矩形的像素宽度(后面会说到这不是一种好方法)。然后,添加以下代码。

    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 d;
        })
        .attr("height",rectHeight-2)
        .attr("fill","steelblue");
    

    这段代码添加了与 dataset 数组的长度相同数量的矩形,所使用的语句是:

    svg.selectAll("rect")   //选择svg内所有的矩形
        .data(dataset)  //绑定数组
        .enter()        //指定选择集的enter部分
        .append("rect") //添加足够数量的矩形元素
    

    这段代码以后会常常出现在 D3 的代码中,请务必牢记。目前不深入讨论它的作用机制是怎样的,只需要读者牢记,当:

    有数据,而没有足够图形元素的时候,使用此方法可以添加足够的元素。

    添加了元素之后,就需要分别给各元素的属性赋值。在这里用到了 function(d, i),前面已经讲过,d 代表与当前元素绑定的数据,i 代表索引号。给属性赋值的时候,是需要用到被绑定的数据,以及索引号的。

    最后一行的:

    .attr("fill","steelblue");
    
    比例尺的使用

    比例尺是 D3 中很重要的一个概念,上一章里曾经提到过直接用数值的大小来代表像素不是一种好方法,本章正是要解决此问题。

    为什么需要比例尺

    上一章制作了一个柱形图,当时有一个数组:

    var dataset = [ 250 , 210 , 170 , 130 , 90 ];
    

    绘图时,直接使用 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 提供了多种比例尺,下面介绍最常用的两种。

    线性比例尺

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

    假设有以下数组:

    var dataset = [1.2, 2.3, 0.9, 1.5, 3.3];
    

    现有要求如下:

    将 dataset 中最小的值,映射成 0;将最大的值,映射成 300。

    代码如下:

    var min = d3.min(dataset);
    var max = d3.max(dataset);
    
    var linear = d3.scaleLinear()
            .domain([min, max])
            .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)。

    序数比例尺

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

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

    我们希望 0 对应颜色 red,1 对应 blue,依次类推。

    但是,这些值都是离散的,线性比例尺不适合,需要用到序数比例尺。

    var ordinal = d3.scaleOrdinal()
            .domain(index)
            .range(color);
    
    ordinal(0); //返回 red
    ordinal(2); //返回 green
    ordinal(4); //返回 black
    
    给柱形图添加比例尺

    修改一下数组,再定义一个线性比例尺。

    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 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");
    

    如此一来,所有的数值,都按照同一个线性比例尺的关系来计算宽度,因此数值之间的大小关系不变。

    坐标轴

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

    image-20190802180149326.png

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

    • 矩形
    • 圆形
    • 椭圆
    • 线段
    • 折线
    • 多边形

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

    • 路径

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

    显然,这里面没有坐标轴 这种元素。如果有的话,我们可以采用类似以下的方式定义:

    <axis x1="" x2="" ...></axis>
    

    很可惜,没有这种元素。但是,这种设计是合理的:不可能为每一种图形都配备一个单独的元素,那样 SVG 就会过于庞大。

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

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

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

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

    定义坐标轴

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

    var dataset = [1, 2, 3, 4, 5];  // 数据源  x
    
    // 比例尺  让图表更加的直观, 合理
    
    var min = d3.min(dataset);
    var max = d3.max(dataset);
    
    // console.log(max)
    
    // scaleLinear可以定义比例尺  domain range
    var linear = d3.scaleLinear().domain([0, max]).range([0, 300]);
    
    // 添加坐标轴
    var xAxis = d3.axisBottom(linear);
    
    在svg中添加坐标轴

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

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

    让你的坐标轴动起来demo

    <!DOCTYPE html>
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
        <style>
            #update {
                position: absolute;
                top: 10px;
                left: 10px;
            }
        </style>
    </head>
    
    <body>
        <button id="update">更新</button>
        <script src="https://d3js.org/d3.v5.js"></script>
        <script>
    
            // 1. 添加画布
            var width = 960;
            var height = 500;
    
            var svg = d3.select('body').append('svg').attr('width', width).attr('height', height)
    
            // 完成 静态的  坐标轴
            // 线性比例尺
            var scale = d3.scaleLinear().domain([0, 100]).range([100, 860]);
    
            var axis = d3.axisBottom(scale);
    
            
            var g = svg.append('g').attr('id', 'g').call(axis);
    
            // 绑定事件
            d3.select('#update').on('click', function() {
                // 更新数据
                scale.domain([0, Math.random() * 100]);
                // g.call(axis);
                // transition d3提供默认的动画
                d3.select('#g').transition().call(axis)
            })
    
        </script>
    </body>
    
    </html>
    
    

    相关文章

      网友评论

        本文标题:D3.js

        本文链接:https://www.haomeiwen.com/subject/kadhohtx.html