可视化工具D3教程

可视化工具D3教程可视化工具 D3 教程第 1 章 D3 简介第 2 章第一个程序 HelloWorld 第 3 章正式进入 D3 的世界第 4 章选择 插入 删除元素第 5 章做一个简单的图表第 6 章比例尺的使用第 7 章坐标轴第 8 章完整的柱形图第 9 章让图表动起来第 10 章理解 update enter exit 第 11 章交互式操作第 12 章布局第 13 章饼状图第 14 章力导向图第 15 章树状图第 16 章地图可视化学习 D

第1章 D3简介

D3
近年来,可视化越来越流行,许多报刊杂志、门户网站、新闻媒体都大量使用可视化技术,使得复杂的数据和文字变得十分容易理解,有一句谚语“一张图片价值于一千个字”。D3 正是数据可视化工具中的佼佼者,基于 JavaScript 开发,项目托管于 GitHub。从 D3诞生以来,不断受到好评,在 GitHub 上的项目仓库排行榜也不断上升。可视化越来越流行,许多报刊杂志、门户网站、新闻、媒体都大量使用可视化技术,使得复杂的数据和文字变得十分容易理解,有一句谚语“一张图片价值于一千个字”,的确是名副其实。各种数据可视化工具也如井喷式地发展,D3 正是其中的佼佼者。D3 的全称是(Data-Driven Documents),顾名思义可以知道是一个被数据驱动的文档。听名字有点抽象,说简单一点,其实就是一个 JavaScript 的函数库,主要是用来做数据可视化。

学习D3前的预备知识

  • HTML:超文本标记语言,用于设定网页的内容
  • CSS:层叠样式表,用于设定网页的样式
  • JavaScript:一种直译式脚本语言,用于设定网页的行为
  • DOM:文档对象模型,用于修改文档的内容和结构
  • SVG:可缩放矢量图形,用于绘制可视化的图形

D3“安装”

D3 是一个 JavaScript 函数库,并不需要通常所说的“安装”。它只有一个文件,在 HTML 中引用即可。目前D3已经更新到V5版本。因为原理是差不多的,所以仍然以V3版本为例作讲解。

  • 下载D3.js文件 D3.zip
  • 加载D3.js文件
//直接包含网络的链接 //加载本地文件 

第2章 第一个程序 Hello World

1.在HTML 中输出 HelloWorld

 
   
   HelloWorld 
   

Hello World

输出结果

 Hello World 

2.用JS更改Hello World

 
   
   HelloWorld 
   

Hello World

输出结果

 SWUSTVIS 

3.用D3更改Hello World

 
   
   HelloWorld 
    
   

Hello World

输出结果

 SWUSTVIS 

第3章 正式进入D3的世界

  1. 选择集

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

 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中所有的rect元素 var id = body.select("#id"); //选择body中id元素 var class = body.select(".class");//选择body中class类元素 

选择元素函数后常用链式表达接其他操作,如:

d3.select("#id").text("SWUSTVIS").attr("font-size","12px"); 
  1. 绑定数据

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

对于datum():
假设有一字符串 SWUSTVIS,要将此字符串分别与三个段落元素绑定,代码如下:

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

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

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

对于data():

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

I love three things in this world.Sun, moon and you. Sun for morning, moon for night, and you forever.

var dataset = ["sun","moon","you"]; 

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

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

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

I love sun I love moon I love you 

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

d ------- data 数据 i ------- index 索引 

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

第4章 选择、插入、删除元素

在这里插入图片描述
1.选择元素
假设在 body 中有三个段落元素:




Sun

Moon

You

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

选择第一个元素

d3.select("body").select("p").style("color","red"); 

在这里插入图片描述
选择第所有元素

d3.select("body").selectAll("p").style("color","red"); 

在这里插入图片描述
选择第二个元素

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

Moon

------------------------------------- d3.select("#moon").style("color","red");

在这里插入图片描述
选择后两个元素

给后两个元素添加 class,

Moon

You

由于需要选择多个元素,要用 selectAll。

d3.selectAll(".myclass").style("color","red"); 

在这里插入图片描述
2.插入元素

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

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

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

append()

d3.select("body").append("p").text("Star"); 

在这里插入图片描述
insert()

d3.select("body").insert("p","#moon").text("Star"); 

在这里插入图片描述
3.删除元素

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

  • remove()
d3.select("#moon").remove(); 

在这里插入图片描述

第5章 做一个简单的图表

柱形图是一种最简单的可视化图标,主要有矩形、文字标签、坐标轴组成。为简单起见,只绘制矩形的部分,用以讲解如何使用 D3 在 SVG 画布中绘图。

画布是什么

之前处理对象都是 HTML 的文字,没有涉及图形的制作。要绘图,首要需要的是一块绘图的“画布”。HTML 5 提供两种强有力的“画布”:SVGCanvas

SVG 绘制的是矢量图,因此对图像进行放大不会失真,可以为每个元素添加 JavaScript 事件处理器。每个图形均视为对象,更改对象的属性,图形也会改变。要注意,在 SVG 中,x 轴的正方向是水平向右,y 轴的正方向是垂直向下的。

SVG
在 canvas 中,一旦图形被绘制完成,它就不会继续得到浏览器的关注。如果其位置发生变化,那么整个场景也需要重新绘制,包括任何或许已被图形覆盖的对象。

添加画布

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); //设定高度 

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

绘制矩形

 
   
    
    
   

矩形的属性,常用的有四个:

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

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

var dataset = [ 250 , 210 , 170 , 130 , 90 ]; //数据(表示矩形的宽度) var width = 300; //画布的宽度 var height = 300; //画布的高度 var svg = d3.select("body") //选择文档中的body元素 .append("svg") //添加一个svg元素 .attr("width", width) //设定宽度 .attr("height", height); //设定高度 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"); 

在这里插入图片描述
PS:横向变纵向?

svg.selectAll("rect") .data(dataset) .enter() .append("rect") .attr("y",function(d,i){ return height - d; }) .attr("x",function(d,i){ return i * rectHeight; }) .attr("height",function(d){ return d; }) .attr("width",rectHeight-2) .attr("fill","steelblue"); 

在这里插入图片描述
PS:上面的例子是值和像素大小是一样的,那么就会出现一个问题(引入比例尺)。

第6章 比例尺的使用

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

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.scale.linear() .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.scale.ordinal() .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"); 

第7章 坐标轴

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

定义坐标轴

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

//数据 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); //指定刻度的数量 

在 SVG 中添加坐标轴

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

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

在这里插入图片描述
设定坐标轴的样式和位置

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

 

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

坐标轴的位置,可以通过 transform 属性来设定。

通常在添加元素的时候就一并设定,写成如下形式:

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

在这里插入图片描述

第8章 完整的柱形图

一个完整的柱形图包含三部分:矩形、文字、坐标轴。本章将对前几章的内容进行综合的运用,制作一个实用的柱形图,内容包括:选择集、数据绑定、比例尺、坐标轴等内容。

在这里插入图片描述
添加 SVG 画布

//画布大小 var width = 400; var height = 400; //在 body 里添加一个 SVG 画布 var svg = d3.select("body") .append("svg") .attr("width", width) .attr("height", height); //画布周边的空白 var padding = {left:30, right:30, top:20, bottom:20}; 

定义数据和比例尺

//定义一个数组 var dataset = [10, 20, 30, 40, 33, 24, 12, 5]; //x轴的比例尺 var xScale = d3.scale.ordinal() .domain(d3.range(dataset.length)) .rangeRoundBands([0, width - padding.left - padding.right]); //y轴的比例尺 var yScale = d3.scale.linear() .domain([0,d3.max(dataset)]) .range([height - padding.top - padding.bottom, 0]); 

定义坐标轴

//定义x轴 var xAxis = d3.svg.axis() .scale(xScale) .orient("bottom"); //定义y轴 var yAxis = d3.svg.axis() .scale(yScale) .orient("left"); 

添加矩形和文字元素

//矩形之间的空白 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; }) .style({ "fill":"#FFF", "text-anchor":"middle" }); 

添加坐标轴的元素

//添加x轴 svg.append("g") .attr("class","axis") .attr("transform","translate(" + padding.left + "," + (height - padding.bottom) + ")") .call(xAxis); //添加y轴 svg.append("g") .attr("class","axis") .attr("transform","translate(" + padding.left + "," + padding.top + ")") .call(yAxis); 

在这里插入图片描述

第9章 让图表动起来

D3 支持制作动态的图表。有时候,图表的变化需要缓慢的发生,以便于让用户看清楚变化的过程。

什么是动态效果

前面几章制作的图表是一蹴而就地出现,然后绘制完成后不再发生变化的,这是静态的图表。动态的图表,是指图表在某一时间段会发生某种变化,可能是形状、颜色、位置等,而且用户是可以看到变化的过程的。

例如,有一个圆,圆心为 (100, 100)。现在我们希望圆的 x 坐标从 100 移到 300,并且移动过程在 2 秒的时间内发生。这种时候就需要用到动态效果,在 D3 里我们称之为过渡(transition)。

实现动态的方法

D3 提供了 4 个方法用于实现图形的过渡:从状态 A 变为状态 B。

  • transition() 启动过渡效果

其前后是图形变化前后的状态(形状、位置、颜色等等),例如:

.attr("fill","red") //初始颜色为红色 .transition() //启动过渡 .attr("fill","steelblue") //终止颜色为铁蓝色 

D3 会自动对两种颜色(红色和铁蓝色)之间的颜色值(RGB值)进行插值计算,得到过渡用的颜色值。我们无需知道中间是怎么计算的,只需要享受结果即可。

  • duration() 指定过渡的持续时间,单位为毫秒。

如 duration(2000) ,指持续 2000 毫秒,即 2 秒。

  • ease()指定过渡的方式,常用的有:
  • delay() 指定延迟的时间,表示一定时间后才开始转变,此函数可以对整体指定延迟,也可以对个别指定延迟。

例如,对整体指定时:

.transition() .duration(1000) .delay(500) 

如此,图形整体在延迟 500 毫秒后发生变化,变化的时长为 1000 毫秒。因此,过渡的总时长为1500毫秒。

又如,对一个一个的图形(图形上绑定了数据)进行指定时:

.transition() .duration(1000) .delay(funtion(d,i){ return 200*i; }) 

如此,假设有 10 个元素,那么第 1 个元素延迟 0 毫秒(因为 i = 0),第 2 个元素延迟 200 毫秒,第 3 个延迟 400 毫秒,依次类推….整个过渡的长度为 200 * 9 + 1000 = 2800 毫秒。

实现简单的动态效果

下面将在 SVG 画布里添加三个圆,圆出现之后,立即启动过渡效果。

第一个圆,要求移动 x 坐标。

var circle1 = svg.append("circle") .attr("cx", 100) .attr("cy", 100) .attr("r", 45) .style("fill","green"); //在1秒(1000毫秒)内将圆心坐标由100变为300 circle1.transition() .duration(1000) .attr("cx", 300); 

第二个圆,要求既移动 x 坐标,又改变颜色。

var circle2 = svg.append("circle") .attr("cx", 100) .attr("cy", 100) .attr("r", 45) .style("fill","green");//与第一个圆一样,省略部分代码 //在1.5秒(1500毫秒)内将圆心坐标由100变为300, //将颜色从绿色变为红色 circle2.transition() .duration(1500) .attr("cx", 300) .style("fill","red"); 

第三个圆,要求既移动 x 坐标,又改变颜色,还改变半径。

var circle3 = svg.append("circle") .attr("cx", 100) .attr("cy", 100) .attr("r", 45) .style("fill","green"); //在2秒(2000毫秒)内将圆心坐标由100变为300 //将颜色从绿色变为红色 //将半径从45变成25 //过渡方式采用bounce(在终点处弹跳几次) circle3.transition() .duration(2000) .ease("bounce") .attr("cx", 300) .style("fill","red") .attr("r", 25); 

给柱形图加上动态效果

在上一章完整柱形图的基础上稍作修改,即可做成一个带动态效果的、有意思的柱形图。在添加文字元素和矩形元素的时候,启动过渡效果,让各柱形和文字缓慢升至目标高度,并且在目标处跳动几次。

对于文字元素,代码如下:

.attr("y",function(d){ var min = yScale.domain()[0]; return yScale(min); }) .transition() .delay(function(d,i){ return i * 200; }) .duration(2000) .ease("bounce") .attr("y",function(d){ return yScale(d); }); 

文字元素的过渡前后,发生变化的是 y 坐标。其起始状态是在 y 轴等于 0 的位置(但要注意,不能在起始状态直接返回 0,要应用比例尺计算画布中的位置)。终止状态是目标值。

在这里插入图片描述

第10章 理解 update()、enter()、exit()

svg.selectAll("rect") //选择svg内所有的矩形 .data(dataset) //绑定数组 .enter() //指定选择集的enter部分 .append("rect") //添加足够数量的矩形元素 
  • update()
    当对应的元素正好满足时 ( 绑定数据数量 = 对应元素 ),实际上并不存在这样一个函数,只是为了要与之后的 enter 和 exit 一起说明才想象有这样一个函数。但对应元素正好满足时,直接操作即可,后面直接跟 text ,style 等操作即可。

  • enter()
    当对应的元素不足时 ( 绑定数据数量 > 对应元素 ),当对应的元素不足时,通常要添加元素,使之与绑定数据的数量相等。后面通常先跟 append 操作。

  • exit()
    当对应的元素过多时 ( 绑定数据数量 < 对应元素 ),当对应的元素过多时,通常要删除元素,使之与绑定数据的数量相等。后面通常要跟 remove 操作。
    在这里插入图片描述




第11章 交互式操作

什么是交互

交互,指的是用户输入了某种指令,程序接受到指令之后必须做出某种响应。对可视化图表来说,交互能使图表更加生动,能表现更多内容。例如,拖动图表中某些图形、鼠标滑到图形上出现提示框、用触屏放大或缩小图形等等。用户用于交互的工具一般有三种:鼠标、键盘、触屏。

如何添加交互
对某一元素添加交互操作十分简单,代码如下:

var circle = svg.append("circle"); circle.on("click", function(){ //在这里添加交互内容 }); 

这段代码在 SVG 中添加了一个圆,然后添加了一个监听器,是通过 on() 添加的。在 D3 中,每一个选择集都有 on() 函数,用于添加事件监听器。

on() 的第一个参数是监听的事件,第二个参数是监听到事件后响应的内容,第二个参数是一个函数。

鼠标事件:

  • click:鼠标单击某元素时,相当于 mousedown 和 mouseup 组合在一起。
  • mouseover:光标放在某元素上。
  • mouseout:光标从某元素上移出来时。
  • mousemove:鼠标被移动的时候。
  • mousedown:鼠标按钮被按下。
  • mouseup:鼠标按钮被松开。
  • dblclick:鼠标双击。

键盘事件:

  • keydown:当用户按下任意键时触发,按住不放会重复触发此事件。该事件不会区分字母的大小写,例如“A”和“a”被视为一致。
  • keypress:当用户按下字符键(大小写字母、数字、加号、等号、回车等)时触发,按住不放会重复触发此事件。该事件区分字母的大小写。
  • keyup:当用户释放键时触发,不区分字母的大小写。 触屏常用的事件有三个:

带有交互的柱形图

将之前的柱形图部分代码修改成如下代码。

var rects = svg.selectAll(".MyRect") .data(dataset) .enter() .append("rect") .attr("class","MyRect") //把类里的 fill 属性清空 .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") //填充颜色不要写在CSS里 .on("mouseover",function(d,i){ d3.select(this) .attr("fill","yellow"); }) .on("mouseout",function(d,i){ d3.select(this) .transition() .duration(500) .attr("fill","steelblue"); }); 

这段代码添加了鼠标移入(mouseover),鼠标移出(mouseout)两个事件的监听器。监听器函数中都使用了 d3.select(this),表示选择当前的元素,this 是当前的元素,要改变响应事件的元素时这么写就好。

mouseover 监听器函数的内容为:将当前元素变为黄色

mouseout 监听器函数的内容为:缓慢地将元素变为原来的颜色(蓝色)

第12章 布局

布局,可以理解成 “制作常见图形的函数”,有了它制作各种相对复杂的图表就方便多了。

在这里插入图片描述
布局是什么

从上面的图可以看到,布局的作用是:将不适合用于绘图的数据转换成了适合用于绘图的数据。因此,为了便于初学者理解,将布局的作用解释成:数据转换。

布局有哪些

D3 总共提供了 12 个布局:饼状图(Pie)、力导向图(Force)、弦图(Chord)、树状图(Tree)、集群图(Cluster)、捆图(Bundle)、打包图(Pack)、直方图(Histogram)、分区图(Partition)、堆栈图(Stack)、矩阵树图(Treemap)、层级图(Hierarchy)。

12 个布局中,层级图(Hierarchy)不能直接使用。集群图、打包图、分区图、树状图、矩阵树图是由层级图扩展来的。如此一来,能够使用的布局是 11 个(有 5 个是由层级图扩展而来)。这些布局的作用都是将某种数据转换成另一种数据,而转换后的数据是利于可视化的。

  • Bundle —捆图
    在这里插入图片描述

  • Chord — 弦图
    在这里插入图片描述

  • Cluster — 集群图
    在这里插入图片描述

  • Force —力学图、力导向图
    在这里插入图片描述

  • Histogram —- 直方图(数据分布图)
    在这里插入图片描述

  • Pack —- 打包图
    在这里插入图片描述

  • Partition —- 分区图
    在这里插入图片描述

  • Pie —- 饼状图
    在这里插入图片描述

  • Stack —- 堆栈图
    在这里插入图片描述

  • Tree —- 树状图
    在这里插入图片描述

  • Treemap —- 矩阵树图
    在这里插入图片描述

第13章 饼状图

本章制作一个饼状图。在布局的应用中,最简单的就是饼状图,通过本文你将对布局有一个初步了解。

在这里插入图片描述
数据

假设有如下数据,需要可视化:

var dataset = [ 30 , 10 , 43 , 55 , 13 ]; 

这样的值是不能直接绘图的。例如绘制饼状图的一个部分,需要知道一段弧的起始角度和终止角度,这些值都不存在于数组 dataset 中。因此,需要用到布局,布局的作用就是计算出适合于作图的数据。

布局(数据转换)

定义一个布局,

var pie = d3.layout.pie(); 

返回值赋给变量 pie,此时 pie 可以当做函数使用。

var piedata = pie(dataset); 

将数组 dataset 作为 pie() 的参数,返回值给 piedata。如此一来,piedata 就是转换后的数据。

绘制图形

为了根据转换后的数据 piedata 来作图,还需要一样工具:生成器。SVG 有一个元素,叫做路径 path,是 SVG 中功能最强的元素,它可以表示其它任意的图形。顾名思义,路径元素就是通过定义一个段“路径”,来绘制出各种图形。但是,路径是很难计算的,通过布局转换后的数据 piedata 仍然很难手动计算得到路径值。为我们完成这项任务的,就是生成器。

这里要用到的叫做弧生成器,能够生成弧的路径,因为饼图的每一部分都是一段弧。

var outerRadius = 150; //外半径 var innerRadius = 100; //内半径,为0则中间没有空白 var arc = d3.svg.arc() //弧生成器 .innerRadius(innerRadius) //设置内半径 .outerRadius(outerRadius); //设置外半径 

弧生成器返回的结果赋值给 arc。此时,arc 可以当做一个函数使用,把 piedata 作为参数传入,即可得到路径值。接下来,可以在 SVG 中添加图形元素了。先在 svg 里添加足够数量(5个)个分组元素(g),每一个分组用于存放一段弧的相关元素。

var arcs = svg.selectAll("g") .data(piedata) .enter() .append("g") .attr("transform","translate("+ (width/2) +","+ (width/2) +")"); 

接下来对每个 g 元素,添加 path 。

arcs.append("path") .attr("fill",function(d,i){ return color(i); }) .attr("d",function(d){ return arc(d); //调用弧生成器,得到路径值 }); 

因为 arcs 是同时选择了 5 个 g 元素的选择集,所以调用 append(“path”) 后,每个 g 中都有 path 。路径值的属性名称是 d,调用弧生成器后返回的值赋值给它。要注意,arc(d) 的参数 d 是被绑定的数据。

另外,color 是一个颜色比例尺,它能根据传入的索引号获取相应的颜色值,定义如下。

var color = d3.scale.category10(); //有十种颜色的颜色比例尺 

然后在每一个弧线中心添加文本。

arcs.append("text") .attr("transform",function(d){ return "translate(" + arc.centroid(d) + ")"; }) .attr("text-anchor","middle") .text(function(d){ return d.data; }); 

arc.centroid(d) 能算出弧线的中心。要注意,text() 里返回的是 d.data ,而不是 d 。因为被绑定的数据是对象,里面有 d.startAngle、d.endAngle、d.data 等,其中 d.data 才是转换前的整数的值。

第14章 力导向图

初始数据如下:

var nodes = [ { name: "桂林" }, { name: "广州" }, { name: "厦门" }, { name: "杭州" }, { name: "上海" }, { name: "青岛" }, { name: "天津" } ]; var edges = [ { source : 0 , target: 1 } , { source : 0 , target: 2 } , { source : 0 , target: 3 } , { source : 1 , target: 4 } , { source : 1 , target: 5 } , { source : 1 , target: 6 } ]; 

布局(数据转换)

定义一个力导向图的布局如下。

var force = d3.layout.force() .nodes(nodes) //指定节点数组 .links(edges) //指定连线数组 .size([width,height]) //指定作用域范围 .linkDistance(150) //指定连线长度 .charge([-400]); //相互之间的作用力 

然后,使力学作用生效:

force.start(); //开始作用 

绘制

有了转换后的数据,就可以作图了。分别绘制三种图形元素:

代码如下:

//添加连线 var svg_edges = svg.selectAll("line") .data(edges) .enter() .append("line") .style("stroke","#ccc") .style("stroke-width",1); var color = d3.scale.category20(); //添加节点 var svg_nodes = svg.selectAll("circle") .data(nodes) .enter() .append("circle") .attr("r",20) .style("fill",function(d,i){ return color(i); }) .call(force.drag); //使得节点能够拖动 //添加描述节点的文字 var svg_texts = svg.selectAll("text") .data(nodes) .enter() .append("text") .style("fill", "black") .attr("dx", 20) .attr("dy", 8) .text(function(d){ return d.name; }); 

调用 call( force.drag ) 后节点可被拖动。force.drag() 是一个函数,将其作为 call() 的参数,相当于将当前选择的元素传到 force.drag() 函数中。

最后,还有一段最重要的代码。由于力导向图是不断运动的,每一时刻都在发生更新,因此,必须不断更新节点和连线的位置。力导向图布局 force 有一个事件 tick,每进行到一个时刻,都要调用它,更新的内容就写在它的监听器里就好。

force.on("tick", function(){ //对于每一个时间间隔 //更新连线坐标 svg_edges.attr("x1",function(d){ return d.source.x; }) .attr("y1",function(d){ return d.source.y; }) .attr("x2",function(d){ return d.target.x; }) .attr("y2",function(d){ return d.target.y; }); //更新节点坐标 svg_nodes.attr("cx",function(d){ return d.x; }) .attr("cy",function(d){ return d.y; }); //更新文字坐标 svg_texts.attr("x", function(d){ return d.x; }) .attr("y", function(d){ return d.y; }); }); 

在这里插入图片描述

第15章 树状图

树状图,可表示节点之间的包含与被包含关系。

现有数据如下:

{ "name":"中国", "children": [ { "name":"浙江" , "children": [ {"name":"杭州" }, {"name":"宁波" }, {"name":"温州" }, {"name":"绍兴" } ] }, { "name":"广西" , "children": [ { "name":"桂林", "children": [ {"name":"秀峰区"}, {"name":"叠彩区"}, {"name":"象山区"}, {"name":"七星区"} ] }, {"name":"南宁"}, {"name":"柳州"}, {"name":"防城港"} ] }, { "name":"黑龙江", "children": [ {"name":"哈尔滨"}, {"name":"齐齐哈尔"}, {"name":"牡丹江"}, {"name":"大庆"} ] }, { "name":"新疆" , "children": [ {"name":"乌鲁木齐"}, {"name":"克拉玛依"}, {"name":"吐鲁番"}, {"name":"哈密"} ] } ] } 

这段数据表示:“中国 – 省份名 – 城市名”的包含于被包含关系。

搭建HTTP服务器(解决Chrome无法读取本地文件)
1.安装Node
2.npm install http-server -g
3.目录下执行 http-server -c-1
4.localhost:8080/xxxx.html








布局(数据转换)

定义一个集群图布局:

var tree = d3.layout.tree() .size([width, height-200]) //设定尺寸 .separation(function(a, b) { return (a.parent == b.parent ? 1 : 2); });//设定节点之间的间隔 

接下来,转换数据:

d3.json("city_tree.json", function(error, root) { var nodes = tree.nodes(root); var links = tree.links(nodes); console.log(nodes); console.log(links); } 

绘制

D3 已经基本上为我们准备好了绘制的函数:d3.svg.diagonal() 。这是一个对角线生成器,只需要输入两个顶点坐标,即可生成一条贝塞尔曲线。

创建一个对角线生成器:

var diagonal = d3.svg.diagonal() .projection(function(d) { return [d.y, d.x]; }); 

projection() 是一个点变换器,默认是 [ d.x , d.y ],即保持原坐标不变,如果写成 [ d.y , d.x ] ,即是说对任意输入的顶点,都交换 x 和 y 坐标。

绘制连线时,使用方法如下:

var link = svg.selectAll(".link") .data(links) .enter() .append("path") .attr("class", "link") .attr("d", diagonal); //使用对角线生成器 

样式

.node circle { fill:#FFF; stroke: steelblue; stroke-width: 1.5px; } .node { font: 12px sans-serif; } .link { fill: none; stroke: #ccc; stroke-width: 1.5px; } 

在这里插入图片描述

第16章 地图可视化

在数据可视化中,地图是很重要的一部分。很多情况会与地图有关联,如中国各省的人口多少,GDP多少等,都可以和地图联系在一起。

D3地图绘制
制作地图需要 JSON 文件,将 JSON 的格式应用于地理上的文件,叫做 GeoJSON 文件。

  • 投影函数
 var projection = d3.geo.mercator()//投影函数 .center([107, 31])//设定地图的中心位置--经度和纬度 .scale(850)//设定放大的比例 .translate([width/2, height/2]);//设定平移 

由于 GeoJSON 文件中的地图数据,都是经度和纬度的信息。它们都是三维的,而要在网页上显示的是二维的,所以要设定一个投影函数来转换经度纬度。如上所示,使用 d3.geo.mercator() 的投影方式。

  • 地理路径生成器

为了根据地图的地理数据生成 SVG 中 path 元素的路径值,需要用到 d3.geo.path(),称为地理路径生成器。

var path = d3.geo.path() .projection(projection); 

projection() 是设定生成器的投影函数,把上面定义的投影传入即可。

  • 加载文件并绘制地图
 d3.json("world.json", function(error, root) { if (error) return console.error(error); console.log(root.features); svg.selectAll("path") .data( root.features ) .enter() .append("path") .attr("stroke","#000") .attr("stroke-width",1) .attr("fill", function(d,i){ return color(i); }) .attr("d", path ) //使用地理路径生成器 .on("mouseover",function(d,i){ d3.select(this) .attr("fill","yellow"); }) .on("mouseout",function(d,i){ d3.select(this) .attr("fill",color(i)); }); }); 

在这里插入图片描述

其他开源地图

  • 高德地图
  • 百度地图
  • 谷歌地图
  • Mapbox

学习D3的站点

  • D3官方网站
  • Mike Bostock 的博客和作品展示板
  • 1897 D3.js Examples

建议

  • 多看
  • 多练
  • 多想
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/198861.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月26日 下午2:19
下一篇 2026年3月26日 下午2:20


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号