可视化工具D3入门

12 个月前 · 来自专栏 数据可视化

D3.js - Data-Driven Documents

D3 的全称是(Data-Driven Documents),顾名思义可以知道是一个被数据驱动的文档。听名字有点抽象,说简单一点,其实就是一个 JavaScript 的函数库,使用它主要是用来做数据可视化的。

入门实例

Html 输出 HelloWorld

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

用JavaScript来改变html中的内容:

<html> 
        <meta charset="utf-8"> 
        <title>HelloWorld</title> 
  </head> 
    <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来更改html:

<html> 
        <meta charset="utf-8"> 
        <title>HelloWorld</title> 
		<script src="https://d3js.org/d3.v7.min.js"></script>
  </head> 
    <p>Hello World 1</p>
    <p>Hello World 2</p>
        <script>
        d3.select("body").selectAll("p").text("D3也可以做的!")  
        </script> 
    </body> 
</html>

可以看出D3更简洁。

选择元素和绑定数据

1. 如何选择元素

•在 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元素

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

2. 如何绑定数据

D3 有一个很独特的功能:能将数据绑定到 DOM 上,也就是绑定到文档上。

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

•datum():绑定一个数据到选择集上

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

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

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

<p>Apple</p>

<p>Pear</p>

<p>Banana</p>

接下来分别使用 datum() 和 data(),将数据绑定到上面三个段落元素上。

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 dogs","I like cats","I like snakes"];

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

Apple I like dogs 绑定

Pear I like cats 绑定

Banana I like snakes 绑定

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

var body = d3.select("body");
var p = body.selectAll("p");
p.data(dataset)
 .text(function(d, i){
 return d;
I like dogs
I like cats
I like snakes
        <meta charset="utf-8">  
        <title>选择元素和绑定数据</title>  
  </head>  
		<p>Apple</p>
		<p>Pear</p>
		<p>Banana</p>
		<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>  
        <script>
        var str = "China";
        var body = d3.select("body");
        var p = body.selectAll("p");
        //使用 datum 绑定单个数据
        /*p.datum(str);
        p.text(function(d, i){
        	return "第 "+ i + " 个元素绑定的数据是 " + d;
        });*/
		//使用 data 绑定数组
		var dataset = ["I like dogs","I like cats","I like snakes"];
		p.data(dataset)
			.text(function(d, i){
				if(i==2)return d;
                                else return this.innerText;
        </script>  
    </body>  
</html> 

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

当 i == 0 时, d 为 I like dogs。

当 i == 1 时, d 为 I like cats。

当 i == 2 时, d 为 I like snakes。

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

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

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

选择、插入、删除元素

选择元素

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

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

选择第一个 p 元素

使用 select ,参数传入 p 即可, 如此返回的是第一个 p 元素。

var p1 = body.select("p");
p1.style("color","red");

选择三个 p 元素

使用 selectAll 选择 body 中所有的 p 元素。

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

选择第二个 p 元素

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

<p id="myid">Pear</p>

然后,使用 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");

•关于 select 和 selectAll 的参数,其实是符合 CSS 选择器的条件的,即用“ 井号(#)”表示 id,用“点(.)”表示 class。

•此外,对于已经绑定了数据的选择集,还有一种选择元素的方法,那就是灵活运用 function(d, i)。 我们已经知道参数 i 是代表索引号的,于是便可以用条件判定语句来指定执行的元素。

插入元素

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

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

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

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

append()

body.append("p")
 .text(“hello");

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

insert()

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

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

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

删除元素

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

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

如此即可删除指定 id 的段落元素。

做一个简单的图表

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

SVG 是什么

1. 画布是什么

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

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

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

•SVG,指可缩放矢量图形(Scalable Vector Graphics),是用于描述二维矢量图形的一种图形格式,是由万维网联盟制定的开放标准。SVG 使用 XML 格式来定义图形 ,除了 IE8 之前的版本外,绝大部分浏览器都支持 SVG,可将 SVG 文本直接嵌入 HTML 中显示。

SVG 有如下特点:

•SVG 绘制的是矢量图,因此对图像进行放大不会失真。

•基于 XML,可以为每个元素添加 JavaScript 事件处理器。

•每个图形均视为对象,更改对象的属性,图形也会改变。

•不适合游戏应用。

Canvas 是什么

•Canvas 是通过 JavaScript 来绘制 2D 图形,是 HTML 5 中新增的元素。

•Canvas 有如下特点:

•绘制的是位图,图像放大后会失真。

•不支持事件处理器。

•能够以 .png 或 .jpg 格式保存图像

•适合游戏应用

添加画布

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

enter()数的作用是返回一个新的D3()对象集合,这个集合包含了所有没有被可视化的数据。这是D3将数据与图形的联系定义的一种模式(enter-update-exit)叫作Enter Mode。

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

svg.selectAll("rect") //选择svg内所有的矩形

.data(dataset) //绑定数组

.enter() //指定选择集的enter部分

.append("rect") //添加足够数量的矩形元素

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

<html>  
	<meta charset="utf-8">  
	<title>做一个简单的图表</title>  
</head> 
<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 = [ 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");
</script>  
</body>  
</html> 

比例尺的使用

比例尺是 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.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,这是按照线性函数的规则计算的。

序数比例尺

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

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

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

var dataset = [ 2.5 , 2.1 , 1.7 , 1.3 , 0.9 ];

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

效果相同

预定义元素-----基本图形

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

•矩形 <rect>

•圆形 <circle>

•椭圆 <ellipse>

•线段 <line>

•折线 <polyline>

•多边形 <polygon>

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

•路径 <path>

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

因此,我们需要用其他元素来组合成坐标轴。为此,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);//指定刻度的数量

•第 1 – 2 行:定义数组。

•第 4 – 7 行:定义比例尺,其中使用了数组 dataset。

•第 9 – 12 行:定义坐标轴,其中使用了线性比例尺 linear。其中:

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

•scale():指定比例尺。

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

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

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

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

设定坐标轴的样式和位置

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

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

让图表动起来

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

1. 什么是动态效果

前面几章制作的图表是一蹴而就地出现,然后绘制完成后不再发生变化的,这是 静态 的图表。

动态 的图表,是指图表在某一时间段会发生某种变化,可能是形状、颜色、位置等,而且用户是可以看到变化的过程的。

例如,有一个圆,圆心为 (100, 100)。现在我们希望圆的 x 坐标 从 100 移到 300 ,并且移动过程在 2 秒 的时间内发生。

这种时候就需要用到动态效果,在 D3 里我们称之为 过渡(transition)

2. 实现动态的方法

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

transition()

启动过渡效果。其前后是图形变化前后的状态(形状、位置、颜色等等),例如:

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

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

duration()

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

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

ease()

•指定过渡的方式,常用的有:

•linear:普通的线性变化

•circle:慢慢地到达变换的最终状态

•elastic:带有弹跳的到达最终状态

•bounce:在最终状态处弹跳几次

•调用时,格式形如: ease(“bounce”)。

delay()

指定延迟的时间,表示一定时间后才开始转变,单位同样为毫秒。此函数可以对整体指定延迟,也可以对个别指定延迟。

•例如,对整体指定时:

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

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

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

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

给柱形图加上动态效果

•做成一个带动态效果的、有意思的柱形图。

•在添加文字元素和矩形元素的时候,启动过渡效果,让各柱形和文字缓慢升至目标高度,并且在目标处跳动几次。

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

<html>  
	<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;
	.MyRect {
		fill: steelblue;
	.MyText {
		fill: white;
		text-anchor: middle;
</style>
</head> 
	<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>  
	<script>
	//画布大小
	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("width", xScale.rangeBand() - rectPadding )
		.attr("y",function(d){
			var min = yScale.domain()[0];
			return yScale(min);
		.attr("height", function(d){
			return 0;
		.transition()
		.delay(function(d,i){
			return i * 200;
		.duration(2000)
		.ease("bounce")
		.attr("y",function(d){
			return yScale(d);
		.attr("height", function(d){
			return height - padding.top - padding.bottom - yScale(d);
	//添加文字元素
	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("dx",function(){
			return (xScale.rangeBand() - rectPadding)/2;
		.attr("dy",function(d){
			return 20;
		.text(function(d){
			return d;
		.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);
	//添加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);
</script>  
</body>  
</html> 


交互式操作

•与图表的交互,指在图形元素上设置一个或多个监听器,当事件发生时,做出相应的反应。

1. 什么是交互

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

用户用于交互的工具一般有三种:鼠标、键盘、触屏。

2. 如何添加交互

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

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

在 D3 中,每一个选择集都有 on() 函数,用于添加事件监听器。

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

鼠标常用的事件有:

•click:鼠标单击某元素时,相当于 mousedown 和 mouseup 组合在一起。

•mouseover:光标放在某元素上。

•mouseout:光标从某元素上移出来时。

•mousemove:鼠标被移动的时候。

•mousedown:鼠标按钮被按下。

•mouseup:鼠标按钮被松开。

•dblclick:鼠标双击。

键盘常用的事件有三个:

•keydown:当用户按下任意键时触发,按住不放会重复触发此事件。该事件不会区分字母的大小写,例如“A”和“a”被视为一致。

•keypress:当用户按下字符键(大小写字母、数字、加号、等号、回车等)时触发,按住不放会重复触发此事件。该事件区分字母的大小写。

•keyup:当用户释放键时触发,不区分字母的大小写。

触屏常用的事件有三个:

•touchstart:当触摸点被放在触摸屏上时。

•touchmove:当触摸点在触摸屏上移动时。

•touchend:当触摸点从触摸屏上拿开时。

当某个事件被监听到时,D3 会把当前的事件存到 d3.event 对象,里面保存了当前事件的各种参数,请大家好好参详。如果需要监听到事件后立刻输出该事件,可以添加一行代码:

circle.on("click", function(){
 console.log(d3.event);
});

带有交互的柱形图

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

完整代码

<html>  
	<meta charset="utf-8">  
	<title>交互式操作</title>  
</head> 
<style>
	.axis path,
	.axis line{
		fill: none;
		stroke: black;
		shape-rendering: crispEdges;
	.axis text {
		font-family: sans-serif;
		font-size: 11px;
	.MyRect {
	.MyText {
		fill: white;
		text-anchor: middle;
</style>
	<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>  
	<script>
	//画布大小
	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")		//填充颜色不要写在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");
	//添加文字元素
	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;
	//添加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);
</script>  
</body>  
</html> 

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

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

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

布局

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

【 D3.js 入门系列 --- 9.1 】 饼状图的制作

饼状图的制作

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

• 这个数据要不能直接用于画饼状图,我们必须通过计算将它转换成角度。这个计算不需要我们手动计算,因为 D3 中提供了 d3.layout.pie() 函数,这个 Layout 就是用于将上面这样的数据转换成饼状图需要的角度。下面定义一个这样的函数。

var pie = d3.layout.pie();

•一定要记住,这是一个函数,使用它的时候,要 pie( dataset ) 这样才转换数据。我们可以先看看转换后输出什么样的数据。

•我们可以先看看转换后输出什么样的数据。

• 如上图所示,5个整数被转换成了5个 Object ,每个里面存有起始角度和结束角度,以及原整数。这样的数据适合做饼状图,这就是 Layout 的作用。但是要注意,实际作图时,还是需要别的作图方法的。

•接下来可以作图了,和前几节一样,都是在 svg 框内作图。上面的有5个整数,也就是有5段弧线。我们先在 svg 里添加5个分组( 也就是 svg 中的元素 g )。每一个分组就是一段弧线。代码如下:

var arcs = svg.selectAll("g")  
              .data(pie(dataset))  
            .enter()  
              .append("g")  
            .attr("transform","translate("+outerRadius+","+outerRadius+")");

•上面的代码中,我们绑定了转换后的数据 pie(dataset) ,有5个数据,所以会添加5个g元素,最后一行代码是移动元素的位置,默认的起始位置是 svg 绘制框的 (0,0) 坐标,也就是左上角。要注意,这个时候上面代码返回的是同时选择5个g元素的选择。

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

arcs.append("path") 
 .attr("fill",function(d,i){ 
 return color(i); 
 .attr("d",function(d){ 
 return arc(d); 
 }); 

因为 arcs 是同时选择5个g元素的,所以 append("pah") 后,是每一个 g 中都有 path ,然后再添加颜色属性,和路径属性。颜色属性的 color(i) 是定义的一个函数。

var color = d3.scale.category10();  

SVG 中的路径属性是 d, 它的值是 arc(d) ,也就是将绑定的数据作为上面定义的函数 arc 的参数算出的值。

接下来在每一个弧线中心添加文本。

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

arc.centroid(d) 能算出弧线的中心,要注意一句代码,返回的是 d.value ,而不是 d,因为当前绑定的数据是 Object,里面有起始角度等值,d.value 是元整数的值,可见上面的截图。

完整代码

<html>    
        <meta charset="utf-8">    
        <title>Pie</title>    
  </head>   
<style>  
</style>  
        <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>    
        <script>  
        var width = 600;  
        var height = 600;  
        var dataset = [ 30 , 10 , 43 , 55 , 13 ];  
        var svg = d3.select("body").append("svg")  
                                .attr("width",width)  
                                .attr("height",height);  
        var pie = d3.layout.pie();  
        var outerRadius = width / 2;  
        var innerRadius = width / 4;  
        var arc = d3.svg.arc()  
                        .innerRadius(innerRadius)  
                        .outerRadius(outerRadius);  
        var color = d3.scale.category10();  
        var arcs = svg.selectAll("g")  
                      .data(pie(dataset))  
                      .enter()  
                      .append("g")  
                      .attr("transform","translate("+outerRadius+","+outerRadius+")");  
        arcs.append("path")  
            .attr("fill",function(d,i){  
                return color(i);  
            .attr("d",function(d){  
                return arc(d);  
        arcs.append("text")  
            .attr("transform",function(d){  
                return "translate(" + arc.centroid(d) + ")";  
            .attr("text-anchor","middle")  
            .text(function(d){  
                return d.value;  
        console.log(dataset);  
        console.log(pie(dataset));  
        </script>    
    </body>    
</html> 

运行截图

力学图的制作

力学图( Force ),也有被翻译做力导向图等。这种图很有意思,先从初始数据开始

var nodes = [ { name: "GuiLin"    },   
              { name: "GuangZhou" },  
              { name: "XiaMen"    },  
              { name: "HangZhou"   },  
              { name: "ShangHai"   },  
              { name: "QingDao"    },  
              { name: "TianJin"    },  
              { name: "BeiJing"    },  
              { name: "ChangChun"  },  
              { name: "XiAn"       },  
              { name: "WuluMuQi"  },  
              { name: "LaSa"       },  
              { name: "ChengDu"    } ];  
var edges = [  { source : 0  , target: 1 } ,  
               { source : 1  , target: 2 } ,  
               { source : 2  , target: 3 } ,  
               { source : 3  , target: 4 } ,  
               { source : 4  , target: 5 } ,  
               { source : 5  , target: 6 } ,  
               { source : 6  , target: 7 } ,  
               { source : 7  , target: 8 } ,  
               { source : 8  , target: 9 } ,  
               { source : 9  , target: 10 } ,  
               { source : 10 , target: 11 } ,  
               { source : 11 , target: 12 } ,  
               { source : 12 , target: 0 } ];    

这里有顶点( nodes )和边( edges ),这里的顶点是一些城市名称,边是两个顶点之间的连线。我们现在要用这些数据来做力学图。但是这样的数据不适合做力学图,比如不知道每一个顶点画在哪个坐标等。所以需要先用 Layout 来转换数据,我们说过, D3 中的 Layout 就是用来转换数据的。 force 的 layout 为:

var force = d3.layout.force()  
                            .nodes(nodes)  
                            .links(edges)  
                            .size([width,height])  
                            .linkDistance(200)  
                            .charge([-100])  
                            .start();  

在上面的代码中:

•d3.layout.force() 是力学图 Layout 的函数

•nodes() 里传入顶点的数组

•links() 里放入边的数组

•size() 是作用域的大小

•linkDistance() 用于设定两个顶点之间的长度

•charge() 是设定弹力的大小。

•start() 表示开始转换

• 调用这个函数后,数据就已经被转换了,我们看看数据从什么转换成什么了:

好了,有了这些数据,我们就可以作图了。我们用 SVG 中的 line 画边,用 SVG 中的 circle 画顶点。

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",10)  
                            .style("fill",function(d,i){  
 return color(i);  
                            .call(force.drag); 

最后一句代码 call( force.drag ) 是设定我们可以拖动顶点。这个 call 函数我们前面说过,这个 call 是用于将当前选择的元素传到 force.drag 函数中。

最后,我们还需要一段代码,如下:

force.on("tick", function(){    
 svg_edges.attr("x1",function(d){ return d.source.x; });  
 svg_edges.attr("y1",function(d){ return d.source.y; });  
 svg_edges.attr("x2",function(d){ return d.target.x; });  
 svg_edges.attr("y2",function(d){ return d.target.y; });  
 svg_nodes.attr("cx",function(d){ return d.x; });  
 svg_nodes.attr("cy",function(d){ return d.y; });  
});  

• tick 指的是时间间隔,也就是每一个时间间隔之后就刷新一遍画面,刷新的内容写在后面的无名函数 function 中, function 函数中写上作图的内容。 我们来看看最终效果图。

力学图 + 人物关系图

人物关系图
主要的问题包括:

•如何在小球旁插入文字

•如何将小球换为别的图形

•如何插入图片

•如何限制小球运动的边界

其中前三点是 SVG 元素的问题,和 D3 无多大关联

1. SVG 图片

SVG 的图片元素的详细解说可看【 官方文档 - 图片 】。通常,我们只需要使用到图片元素的五个属性就够了。

<image xlink:href="image.png" x="200" y="200" width="100" height="100"></image>

其中:

•xlink:href – 图片名称或图片网址

•x – 图片坐上角 x 坐标

•y – 图片坐上角 y 坐标

•width – 图片宽度

•height- 图片高度

在 D3 中插入图片,代码形如:

svg.selectAll("image")
    .data(dataset)
    .enter()
    .append("image")
    .attr("x",200)
    .attr("y",200)
    .attr("width",100)
    .attr("height",100)
    .attr("xlink:href","image.png");

2. SVG 文本

•SVG 的文本元素和图片类似,详细属性见【 官方文档 - 文本 】。

<text x="250" y="150" dx="10" dy="10" font-family="Verdana" font-size="55" fill="blue" >Hello</text>

其中:

•x – 文本 x 坐标

•y – 文本 y 坐标

•dx- x 轴方向的文本平移量

•dy- y 轴方向的文本平移量

•font-family – 字体

•font-size – 字体大小

•fill – 字体颜色

在 D3 中插入文本,代码形如:

svg.selectAll("text")
    .data(dataset)
    .enter()
    .append("text")
    .attr("x",250)
    .attr("y",150)
    .attr("dx",10)
    .attr("dy",10)
    .text("Hello");

3. 源文件

接下来制作力学图的源文件,本次将数据写入 JSON 文件中。

{
"nodes":[
{ "name": "林黛玉" , "image" : "林黛玉.jpg" },
{ "name": "贾敏" , "image" : "贾敏.jpg" },
{ "name": "贾母" , "image" : "贾母.jpg" },
{ "name": "贾赦" , "image" : "贾赦.jpg" }
"edges":[
{ "source": 0 , "target": 1 , "relation":"母女" },
{ "source": 0 , "target": 2 , "relation":"孙女" },
{ "source": 1 , "target": 2 , "relation":"母女" },
{ "source": 1 , "target": 3 , "relation":"兄妹" },
{ "source": 2 , "target": 3 , "relation":"母子" }
}

力学图

1 读入文件

读入 JSON 文件

d3.json("relation.json",function(error,root){ 
 if( error ){
 return console.log(error);
 console.log(root);
}

4.2 定义力学图的布局

力学图的 Layout(布局)如下:

 var force = d3.layout.force()
 .nodes(root.nodes)
 .links(root.edges)
 .size([width,height])
 .linkDistance(200)
 .charge(-1500)
 .start();

其中 linkDistance 是结点间的距离, charge 是定义结点间是吸引(值为正)还是互斥(值为负),值越大力越强。

4.3 绘制连接线

绘制结点之间的连接线的代码如下:

 var edges_line = svg.selectAll("line")
 .data(root.edges)
 .enter()
 .append("line")
 .style("stroke","#ccc")
 .style("stroke-width",1);
  var edges_text = svg.selectAll(".linetext")
 .data(root.edges)
 .enter()
 .append("text")
 .attr("class","linetext")
 .text(function(d){
 return d.relation;
 });

其中,
第 1 – 6 行:绘制直线
第 8 – 15 行:绘制直线上的文字

直线上文字的样式为:

.linetext {
    font-size: 12px ;
    font-family: SimSun;
    fill:#0000FF;
    fill-opacity:0.0;
}

fill-opacity 是透明度,0表示完全透明,1表示完全不透明。这里是0,表示初始状态下不显示。

4.4 绘制结点

绘制结点的图片和文字:

  var nodes_img = svg.selectAll("image") 
  .data(root.nodes)
 .enter()
 .append("image")
 .attr("width",img_w)
 .attr("height",img_h)
 .attr("xlink:href",function(d){//绘制图片
 return d.image; })
 .on("mouseover",function(d,i){ //显示连接线上的文字
 edges_text.style("fill-opacity",function(edge){
 if( edge.source === d || edge.target === d ){
 return 1.0;
 .on("mouseout",function(d,i){ //隐去连接线上的文字
 edges_text.style("fill-opacity",function(edge){
 if( edge.source === d || edge.target === d ){
 return 0.0;
 .call(force.drag); 
 var text_dx = -20; var text_dy = 20; 
 var nodes_text = svg.selectAll(“.nodetext”) //节点文字,绘制图片下方的文字
 .data(root.nodes)
 .enter()
 .append("text")
 .attr("class","nodetext")
 .attr("dx",text_dx)
 .attr("dy",text_dy)
 .text(function(d){
 return d.name;
});

5 更新

设定力学图更新时调用的函数,使用 force.on ("tick", function(){ }),表示每一步更新都调用 function 函数。
 force.on("tick", function(){ 
 //限制结点的边界
 root.nodes.forEach(function(d,i){
 d.x = d.x - img_w/2 < 0 ? img_w/2 : d.x ;
 d.x = d.x + img_w/2 > width ? width - img_w/2 : d.x ;
 d.y = d.y - img_h/2 < 0 ? img_h/2 : d.y ;
 d.y = d.y + img_h/2 + text_dy > height ? height - img_h/2 - text_dy : d.y ;
 //更新连接线的位置
 edges_line.attr("x1",function(d){ return d.source.x; });
 edges_line.attr("y1",function(d){ return d.source.y; });
 edges_line.attr("x2",function(d){ return d.target.x; });
 edges_line.attr("y2",function(d){ return d.target.y; }); 
 //更新连接线上文字的位置
 edges_text.attr("x",function(d){ return (d.source.x + d.target.x) / 2 ; });
 edges_text.attr("y",function(d){ return (d.source.y + d.target.y) / 2 ; }); 
 //更新结点图片和文字
 nodes_img.attr("x",function(d){ return d.x - img_w/2; });
 nodes_img.attr("y",function(d){ return d.y - img_h/2; });
 nodes_text.attr("x",function(d){ return d.x });
 nodes_text.attr("y",function(d){ return d.y + img_w/2; });
 });


var force = d3.layout.force() .nodes(root.nodes) .links(root.edges) .size([width,height]) .linkDistance(200) .charge(-1500) .start();
force.on("tick", function(){
 });

这里的 force 是之前代码中定义的布局( Layout ),tick 表示当运动进行中每更新一帧时的事件。这是力学图中最常使用的事件,用于设定力学图每一帧是如何更新的。除此之外,还有一些其他常用的事件。

代码中,假设定义如下的布局:

 var force = d3.layout.force()
 .size([width,height])
 .linkDistance(200)
 .charge(-1500);
力学图布局force本身的事件,D3提供了3个,分别为start ,end,tick。在写代码时,可形如:
 //力学图运动开始时
 force.on("start", function(){
 console.log("开始");
 });

出现了以下代码:

.call(force.drag);

即设定当拖拽时调用函数 force.drag()。

D3中提供了3种拖拽事件:dragstart、dragend、drag。

 var drag = force.drag()
 .on("dragstart",function(d,i){
 console.log("拖拽状态:开始");
 })

完整代码:

<html>
        <meta charset="utf-8">  
        <title>Force</title>  
  </head> 
<style>
.nodetext {
	font-size: 12px ;
	font-family: SimSun;
	fill:#000000;
.linetext {
	font-size: 12px ;
	font-family: SimSun;
	fill:#0000FF;
	fill-opacity:0.0;
</style>
		<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>  
        <script>		   
		var width = 600;
		var height = 600;
		var img_w = 77;
		var img_h = 90;
		var svg = d3.select("body").append("svg")
				.attr("width",width)
				.attr("height",height);	
		d3.json("relation.json",function(error,root){			
			if( error ){
				return console.log(error);
			console.log(root);
			var force = d3.layout.force()
					.nodes(root.nodes)
					.links(root.edges)
					.size([width,height])
					.linkDistance(200)
					.charge(-1500)
					.start();
			var edges_line = svg.selectAll("line")
					.data(root.edges)
					.enter()
					.append("line")
					.style("stroke","#ccc")
					.style("stroke-width",1);
			var edges_text = svg.selectAll(".linetext")
					.data(root.edges)
					.enter()
					.append("text")
					.attr("class","linetext")
					.text(function(d){
						return d.relation;
			var nodes_img = svg.selectAll("image")
					.data(root.nodes)
					.enter()
					.append("image")
					.attr("width",img_w)
					.attr("height",img_h)
					.attr("xlink:href",function(d){
						return d.image;
					.on("mouseover",function(d,i){
						//显示连接线上的文字
						edges_text.style("fill-opacity",function(edge){
						       if( edge.source === d || edge.target === d ){
							return 1.0;
					.on("mouseout",function(d,i){
						//隐去连接线上的文字
						edges_text.style("fill-opacity",function(edge){
							if( edge.source === d || edge.target === d ){
								return 0.0;
					.call(force.drag);
			var text_dx = -20;
			var text_dy = 20;
			var nodes_text = svg.selectAll(".nodetext")
					.data(root.nodes)
					.enter()
					.append("text")
					.attr("class","nodetext")
					.attr("dx",text_dx)
					.attr("dy",text_dy)
					.text(function(d){
						return d.name;
			force.on("tick", function(){
				//限制结点的边界
				root.nodes.forEach(function(d,i){
					d.x = d.x - img_w/2 < 0     ? img_w/2 : d.x ;
					d.x = d.x + img_w/2 > width ? width - img_w/2 : d.x ;
					d.y = d.y - img_h/2 < 0      ? img_h/2 : d.y ;
					d.y = d.y + img_h/2 + text_dy > height ? height - img_h/2 - text_dy : d.y ;
				//更新连接线的位置
				 edges_line.attr("x1",function(d){ return d.source.x; });
				 edges_line.attr("y1",function(d){ return d.source.y; });
				 edges_line.attr("x2",function(d){ return d.target.x; });
				 edges_line.attr("y2",function(d){ return d.target.y; });
				 //更新连接线上文字的位置
				 edges_text.attr("x",function(d){ return (d.source.x + d.target.x) / 2 ; });
				 edges_text.attr("y",function(d){ return (d.source.y + d.target.y) / 2 ; });
				 //更新结点图片和文字
				 nodes_img.attr("x",function(d){ return d.x - img_w/2; });
				 nodes_img.attr("y",function(d){ return d.y - img_h/2; });
				 nodes_text.attr("x",function(d){ return d.x });
				 nodes_text.attr("y",function(d){ return d.y + img_w/2; });
        </script> 		
    </body>  
</html> 

地图的绘制 [2]

•地图的制作在 D3 中可以说是最重要的一环。因为在进行数据可视化时,很多情况都会和地图联系在一起,如中国各省的人口多少,GDP多少等,都可以和地图联系在一起。

• D3 中制作地图所需要的文件问 JSON 文件。JSON( JavaScript Object Notation) 是一种轻量级的数据交换格式。关于 JSON 的语法格式,可以在:

w3school.com.cn/json/ http://www.w3school.com.cn/json/

•将 JSON 的格式应用于地理上的文件,叫做 GeoJSON 文件。本节就是用这种文件绘制地图。

•中国地图的 GeoJSON 文件: • china.json

china.json
581.6K
·
百度网盘

这个地图文件 china.json [3] 下载下来了,但不是原地图文件,所以后面图形没法展示!

•这个文件是用 Natural Earth 上的数据,经过提取后制作而成。

开始用 D3 来绘制地图吧。绘制分为三步:

1. 设定投影函数

var projection = d3.geo.mercator()  
                        .center([107, 31])  
                        .scale(850)  
                        .translate([width/2, height/2]); 

由于 GeoJSON 文件中的地图数据,都是经度和纬度的信息,它们都是三维的。要在网页上显示的是二维的,所以要设定一个投影函数来转换经度纬度。如上所示,我们用 d3.geo.mercator() 的投影方式。关于各种投影方式的函数,可以参考: github.com/mbostock/d3/

center() 函数是用于设定地图的中心位置,[107,31] 指的是经度和纬度。

scale() 函数用于设定放大的比例。

translate() 函数用于设定平移。

2. 设定 path 函数

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

将上面的投影函数,作为参数,放入 path 中。这个 path 函数后面在绘制的时候就会用于转换经度纬度为平面信息,用于绘制。

3. 读取文件并绘制

d3.json("china.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");