一、简介
1、定义
D3 的全称是(Data-Driven Documents),顾名思义可以知道是一个被数据驱动的文档。 一个 JavaScript 的函数库,使用它主要是用来做数据可视化的。
2、数据可视化
3、地位
在关于2014年流行的JavaScript图形库的调查中D3排名第5,高于JQuery。
4、D3使用
D3是一个JavaScript函数库,并不需要通常所说的“安装”。它只是一个文件,在HTML中引用即可。有两种方法:
(1)下载 D3.js 的文件
解压后,在 HTML 文件中包含相关的 js 文件即可
(2)还可以直接包含网络的链接,这种方法较简单:
但使用的时候要保持网络连接有效,不能再断网的情况下使用。
5、需要工具
制作网页常用的工具即可。
(1)记事本软件:Notepad++、Editplus、Sublime Text 等,选择自己喜欢的即可。
(2)浏览器:IE9 以上、Firefox、Chrome等,推荐用 Chrome 。
二、D3.js选择,绑定,插入数据
(一)选择元素
1、
d3.select():是选择所有指定元素的第一个
select("tag") 通过元素的标签名称来找查元素
实例:select("input")
select("#id")通过元素的id来找查元素
实例:select("#mydiv")
select(".myclass")通过sytle样式来找查元素
select(".mystyle")
2、
d3.selectAll():是选择指定元素的全部
例子:
(1)var body=d3.select("body");//
选择文档中的body元素
(2)var p1=body.select("p");
//选择body中的第一个p元素
(3)var p =body.selectAll("p");
//选择body中的所有p元素var
(4)var svg=body.select("svg");
//选择body中的svg元素
(5)var rects=svg.selectAll("rect");
//选择svg中所有的rect元素
(二)绑定数据
D3能将数据绑定到
DOM(文件对象模型)上,也是绑定到
文档上
1、datum( ):绑定一个数据到选择集上
datum()实例:
<html>
<head>
<meta charset="utf-8">
<title>datum</title>
</head>
<body>
<p>Apple</p>
<p>Pear</p>
<p>Banana</p>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
var str = "China";
var body = d3.select("body");
var p = body.selectAll("p");
p.datum(str);
p.text(function(d, i){
return "第 "+ i + " 个元素绑定的数据是 " + d;});
</script>
</body>
</html>
结果:
第 0 个元素绑定的数据是 China
第 1 个元素绑定的数据是 China
第 2 个元素绑定的数据是 China
2、data( ):绑定一个数组到选择集上,数组的各项值分别与选择集的各元素绑定
data( )实例:
<html>
<head>
<meta charset="utf-8">
<title>datum</title>
</head>
<body>
<p>Apple</p>
<p>Pear</p>
<p>Banana</p>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
var dataset = ["I like dogs","I like cats","I like snakes"];
var body = d3.select("body");
var p = body.selectAll("p");
p.data(dataset)
.text(function(d, i){
return d;
});
</script>
</body>
</html>
结果
I like dogs
I like cats
I like snakes
3、对于已经绑定了数据的选择集,还有一种选择元素的方法,
运用 function(d, i)。
参数 i 是代表索引号,可以用条件判定语句来指定执行的元素。
(三)插入元素
1、append( ):在选择集末尾插入元素
实例:
<html>
<head>
<meta charset="utf-8">
<title>append</title>
</head>
<body>
<p>Apple</p>
<p id=myid>Pear</p>
<p>Banana</p>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
d3.select("body").append("p").text("append p element");
</script>
</body>
</html>
结果:
Apple
Pear
Banana
append p element
2、insert( ):在选择集前面插入元素
实例:
<html>
<head>
<meta charset="utf-8">
<title>insert</title>
</head>
<body>
<p id=myid>Apple</p>
<p >Pear</p>
<p>Banana</p>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
var body=d3.select("body").insert("p","#myid").text("insert p element");
var p=body.select("#myid");
</script>
</body>
结果:
insert p element
Apple
Pear
Banana
三、画图
(一)、画布概述
要绘图,首先需要的是一块绘图的“画布”。
HTML5提供两种强有力的“画布”:
1、SVG(scalable vector graphics)可缩放矢量图形
(1)定义
是用于描述二维矢量图形的一种图形格式,是由万维网联盟制定的开放标准。SVG 使用 XML 格式来定义图形,除了 IE8 之前的版本外,绝大部分浏览器都支持 SVG,可将 SVG 文本直接嵌入 HTML 中显示。
(2)特点
SVG 绘制的是矢量图,因此对图像进行放大不会失真。
基于 XML,可以为每个元素添加 JavaScript 事件处理器。
每个图形均视为对象,更改对象的属性,图形也会改变。
不适合游戏应用。
2、Canvas 帆布
(1)定义
Canvas是通过JavaScript来绘制2D图形,是HTML5中新增的元素。
(2)特点
绘制的是位图,图像放大后会失真。
不支持事件处理器。
能够以.png或.jpg格式保存图像。
适合游戏应用。
3、注意
D3 虽然没有明文规定一定要在 SVG 中绘图,但是 D3 提供了众多的 SVG 图形的生成器,它们都是只支持 SVG 的。因此,建议使用 SVG 画布。
(二)画布添加
1、
使用 D3 在
body 元素
中
添加 svg
var width=300; //画布的宽度
var height=300; //画布的高度
var svg=d3.select("body"); //选择文档中的body元素
.append("svg"); //添加一个SVG元素
.attr("width",width); //设定高度
.attr("height",height); //设定高度
2、实例:绘制矩形
注意:
(1)矩形的属性,常用的有四个:
x:矩形左上角的 x 坐标
y:矩形左上角的 y 坐标
width:矩形的宽度
height:矩形的高度
(2)在 SVG 中,x 轴的正方向是水平向右,y 轴的正方向是垂直向下的。
代码:
<html>
<head>
<meta charset="utf-8">
<title>绘制矩形</title>
</head>
<body>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
var width = 300; //画布的宽度300
var height = 300; //画布的高度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() //指定选择集的enter部分
.append("rect") //添加足够数量的矩形
.attr("x",20) //取x 坐标为20
.attr("y",function(d,i){ //取y坐标
return i * rectHeight;
})
.attr("width",function(d){ //设定宽度
return d;
})
.attr("height",rectHeight-2) //设定高度
.attr("fill","steelblue"); //以铁蓝色满填充矩形
</script>
</body>
</html>
结果:
(三)比例尺
1. 为什么需要比例尺
var dataset=[250,210,170,130,90]
绘图时,直接使用 250 给矩形的宽度赋值,即矩形的宽度就是 250 个像素。 此方式非常具有局限性,如果数值过大或过小。
var dataset_1=[2.5,2.1,1.7,1.3,0.9]
对以上两个数组,绝不可能用 2.5 个像素来代表矩形的宽度,那样根本看不见;也不可能用 2500 个像素来代表矩形的宽度,因为画布没有那么长
。
比例尺是 D3 中很重要的一个概念,直接用数值的大小来代表像素不是一种好方法
将某一区域的值映射到另一区域,其大小关系不变。这就是比例尺(Scale)。
2、有哪些比例尺
在数学中,x 的范围被称为
定义域,y 的范围被称为
值域。
D3 中的比例尺,也有定义域和值域,分别被称为
domain 和
range。
开发者需要指定 domain 和 range 的范围,如此即可得到一个计算关系。
(1)线性比例尺
能将一个连续的区间,映射到另一区间。
如:
var dataset=[1.2, 2.3, 0.9, 1.5, 3.3]
将 dataset 中最小的值,映射成 0;将最大的值,映射成 300。
实例:
<html>
<head>
<meta charset="utf-8">
<title>线性比例尺</title>
</head>
<body>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
//将 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]).range([0,300]);
alert("linear(0.9):"+linear(0.9));
alert("linear(2.3):"+linear(2.3));
alert("linear(3.3):"+linear(3.3));
</script>
</body>
</html>
其中,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)。
结果:
linear(0.9); //返回 0
linear(2.3); //返回 175
linear(3.3); //返回 300
(2)序数比例尺
用于:
定义域和值域不是连续的。
实例:
<html>
<head>
<meta charset="utf-8">
<title>序数比例尺</title>
</head>
<body>
<script src="js/d3.min.js" charset="utf-8"></script>
</body>
<script>
var index=[0,1,2,3,4];
var color=["red","blue","green","yellow","black"];
var ordinal=d3.scale.ordinal()
.domain(index)
.range(color);
alert(ordinal(0)); 返回 red
alert(ordinal(2)); //返回 green
alert(ordinal(4)); //返回 black
</script>
</html>
结果:
ordinal(0); //返回 red
ordinal(2); //返回 green
ordinal(4); //返回 black
3.实例: 给柱形图添加比例尺
<html>
<head>
<meta charset="utf-8">
<title>给柱形图添加比例尺</title>
</head>
<body>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
var width = 300; //画布的宽度300
var height = 300; //画布的高度300
var svg = d3.select("body") //选择文档中的body元素
.append("svg") //添加一个svg元素
.attr("width", width) //设定宽度
.attr("height", height); //设定高度
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() //指定选择集的enter部分
.append("rect") //添加足够数量的矩形
.attr("x",20) //取x 坐标为20
.attr("y",function(d,i){ //取y坐标
return i * rectHeight;
})
.attr("width",function(d){ //设定宽度
return linear(d);
})
.attr("height",rectHeight-2) //设定高度
.attr("fill","steelblue"); // 以铁蓝色满填充矩形
</script>
</body>
</html>
结果:
(四)坐标轴
1、定义
坐标轴,是可视化图表中经常出现的一种图形,由一些列线段和刻度组成。
坐标轴在 SVG 中是没有现成的图形元素的,需要用其他的元素组合构成。
D3 提供了坐标轴的组件,如此在 SVG 画布中绘制坐标轴变得像添加一个普通元素一样简单。
![]()
2、坐标轴的构成
在 SVG 画布的预定义元素里,有六种基本图形:
矩形 <rect>
圆形 <circle>
椭圆 <ellipse>
线段 <line>
折线 <polyline>
多边形 <polygon>
路径 <path>
实例1:给矩形增加坐标轴
<html>
<head>
<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;
}
</style>
<body>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
var width = 300; //画布的宽度300
var height = 300; //画布的高度300
var svg = d3.select("body") //选择文档中的body元素
.append("svg") //添加一个svg元素
.attr("width", width) //设定宽度
.attr("height", height); //设定高度
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() //指定选择集的enter部分
.append("rect") //添加足够数量的矩形
.attr("x",20) //取x 坐标为20
.attr("y",function(d,i){ //取y坐标
return i * rectHeight;
})
.attr("width",function(d){ //设定宽度
return linear(d);
})
.attr("height",rectHeight-2) //设定高度
.attr("fill","steelblue"); // 以铁蓝色满填充矩形
var axis = d3.svg.axis() //D3 中坐标轴的组件,能够在 SVG 中生成组成坐标轴的元素。
.scale(linear) //指定比例尺
.orient("bottom")//指定刻度的方向bottom 表示在坐标轴的下方显示。
.ticks(7); //指定刻度的数量
svg.append("g")
.attr("class","axis")
.attr("transform","translate(20,130)")
.call(axis);
</script>
</body>
</html>
实例2:完整的柱形图
一个完整的柱形图包括三个部分:矩形,文字,坐标轴
<html>
<head>
<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 {
fill: steelblue;
}
.MyText {
fill: white;
text-anchor: middle;
}
</style>
<body>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
//1、添加 SVG 画布
//画布大小
var width = 400; //画布的宽度
var height = 400; //画布的高度
//在 body 里添加一个 SVG 画布
var svg = d3.select("body") //选择文档中的body元素
.append("svg") //添加一个svg元素
.attr("width", width) //设定宽度
.attr("height", height); //设定高度
//画布周边的空白
var padding={left:30,right:30,top:20,bottom:20}
//2、定义数据和比例尺
//定义一个数组
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]);
//3. 定义坐标轴
//定义X轴
var xAxis=d3.svg.axis()
.scale(xScale)
.orient("bottom");
//定义Y轴
var yAxis=d3.svg.axis()
.scale(yAxis)
.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);
});
//4、添加文字元素
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;
});
//5、添加坐标轴的元素
//添加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>
结果:
![]()
(五)让图表动起来
D3 支持制作动态的图表。有时候,图表的变化需要缓慢的发生,以便于让用户看清楚变化的过程,也能给用户不小的友好感。
1、什么是动态图
动态的图表,是指图表在某
一时间段会发生某种变化,可能是形状、颜色、位置等,而且用户是可以看到变化的过程的。
例如,有一个圆,圆心为 (100, 100)。现在我们希望圆的 x 坐标从 100 移到 300,并且移动过程在 2 秒的时间内发生。 这种时候就需要用到动态效果,在 D3 里我们称之为过渡(transition)。
2、实现动态的方法
D3 提供了 4 个方法用于实现图形的过渡:从状态 A 变为状态 B。
1、transition()
D3 会自动对两种颜色(红色和铁蓝色)之间的颜色值(RGB值)进行插值计算,得到过渡用的颜色值。我们无需知道中间是怎么计算的,只需要结果即可。
.attr("fill","red") //初始颜色为红色
.transition
() //启动过渡
.attr("fill","steelblue") //终止颜色为铁蓝色
2、
duration() 指定过渡的持续时间,单位为毫秒。
3、
ease()
指定过渡的方式,常用的有:
linear:普通的线性变化
circle:慢慢地到达变换的最终状态
elastic:带有弹跳的到达最终状态
bounce:在最终状态处弹跳几次
调用时,格式形如: ease(“bounce”)。
4、
delay() 指定延迟的时间,表示一定时间后才开始转变,单位同样为毫秒。
此函数可以对整体指定延迟,也可以对个别指定延迟。
5、实例:实现简单的动态效果
<html>
<head>
<meta charset="utf-8">
<title>实现简单的动态效果</title>
</head>
<body>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
var width = 700; //画布的宽度300
var height = 700; //画布的高度300
var svg = d3.select("body") //选择文档中的body元素
.append("svg") //添加一个svg元素
.attr("width", width) //设定宽度
.attr("height", height); //设定高度
//圆1
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);
//圆2
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");
//圆3
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);
</script>
</body>
</html>
6、实例:给柱形图加上动态效果
<html>
<head>
<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 {
fill: steelblue;
}
.MyText {
fill: white;
text-anchor: middle;
}
</style>
<body>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
//1、添加 SVG 画布
//画布大小
var width = 400; //画布的宽度
var height = 400; //画布的高度
//在 body 里添加一个 SVG 画布
var svg = d3.select("body") //选择文档中的body元素
.append("svg") //添加一个svg元素
.attr("width", width) //设定宽度
.attr("height", height); //设定高度
//画布周边的空白
var padding={left:30,right:30,top:20,bottom:20}
//2、定义数据和比例尺
//定义一个数组
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]);
//3. 定义坐标轴
//定义X轴
var xAxis=d3.svg.axis()
.scale(xScale)
.orient("bottom");
//定义Y轴
var yAxis=d3.svg.axis()
.scale(yAxis)
.orient("left")
//矩形之间的空白处
var rectPadding=4
//添加矩形元素
var rects=svg.selectAll(".MyRect")//选择svg中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);
})
//4、添加文字元素
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;
})
.attr("y",function(d){//变化前y的坐标
var min = yScale.domain()[0];
return yScale(min);
})
.transition()//过渡
.delay(function(d,i){
return i * 200;
})
.duration(2000)
.ease("bounce")
.attr("y",function(d){//变化后的y坐标
return yScale(d);
});
//5、添加坐标轴的元素
//添加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>
(七)Update、Enter、Exit
1、Update、Enter、Exit 是 D3 中三个非常重要的概念,它处理的是当选择集和数据的数量关系不确定的情况。
2、假设,在 body 中有三个 p 元素,有一数组 [3, 6, 9],则可以将数组中的每一项分别与一个 p 元素绑定在一起。但是,有一个问题:当数组的长度与元素数量不一致(数组长度 > 元素数量 or 数组长度 < 元素数量)时呢?这时候就需要理解 Update、Enter、Exit 的概念。
3、如果数组为 [3, 6, 9, 12, 15],将此数组绑定到三个 p 元素的选择集上。可以想象,会有两个数据没有元素与之对应,这时候 D3 会建立两个空的元素与数据对应,这一部分就称为 Enter。而有元素与数据对应的部分称为 Update。如果数组为 [3],则会有两个元素没有数据绑定,那么没有数据绑定的部分被称为 Exit。示意图如下所示。
4、update 部分的处理办法一般是:更新属性值 enter 部分的处理办法一般是:添加元素后,赋予属性值
(八)交互式操作
与图表的交互,指在图形元素上设置一个或多个监听器,当事件发生时,做出相应的反应。
1、什么是交互
交互,指的是用户输入了某种指令,程序接受到指令之后必须做出某种响应。对可视化图表来说,交互能使图表更加生动,能表现更多内容。
例如,拖动图表中某些图形、鼠标滑到图形上出现提示框、用触屏放大或缩小图形等等。
用户用于交互的工具一般有三种:鼠标、键盘、触屏。
2、如何添加交互
触屏常用的事件有三个: touchstart:当触摸点被放在触摸屏上时。
touchmove:当触摸点在触摸屏上移动时。
touchend:当触摸点从触摸屏上拿开时。
当某个事件被监听到时,D3 会把当前的事件存到 d3.event 对象,里面保存了当前事件的各种参数
var circle = svg.append("circle");
circle.on("click", function(){
//在这里添加交互内容
console.log(d3.event); });
实例:带有交互的柱形图
<html>
<head>
<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 {
fill: steelblue;
}*/
.MyText {
fill: red;
text-anchor: middle;
}
</style>
<body>
<script src="js/d3.min.js" charset="utf-8"></script>
<script>
//画布大小
var width = 400;
var height = 400;
//在 body 里添加一个 SVG 画布
var svg = d3.select("body")//选择d3文档中的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)) //有几个矩形就有几个x刻度
.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")//选择svg中MyRect类中的所有元素
.data(dataset)
.enter()
.append("rect")//添加矩形元素
.attr("class","MyRect")//清除里面的所有类,并设为自己的类myrect
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.attr("x", function(d,i){//矩形左上角的x坐标
return xScale(i) + rectPadding/2;
} )
.attr("width", xScale.rangeBand() - rectPadding )//矩形的宽
.attr("y",function(d){//变化前的矩形左上角的y坐标
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){//矩形左上角的y坐标
return yScale(d);
})
.attr("height", function(d){//矩形的高
return height - padding.top - padding.bottom - yScale(d);
})
//坐标说明位置,宽高说明大小
var rects = svg.selectAll(".Myrect")//选择svg中的MyRect类中的所有元素
.attr("fill","steelblue") //填充颜色不要写在CSS里
.on("mouseover",function(d,i){//鼠标在上,颜色变黄
d3.select(this)
.attr("fill","yellow");
})
.on("mouseout",function(d,i){//鼠标移出,颜色变钢铁蓝
d3.select(this)
.transition()//500毫秒渐变
.duration(500)
.attr("fill","steelblue");
});
//添加文字元素
var texts = svg.selectAll(".MyText")//选择svg中的MyText类中的所有元素
.data(dataset)
.enter()
.append("text")//添加text元素
.attr("class","MyText")//清除里面的所有类,并设为自己的类MyText
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.attr("x", function(d,i){//矩形左上角的x坐标
return xScale(i) + rectPadding/2;
} )
.attr("y",function(d){//矩形左上角的y坐标
return yScale(d);
})
.attr("dx",function(){//text相对于矩形的横向偏移量
return (xScale.rangeBand() - rectPadding)/2;
})
.attr("dy",function(d){//text相对于矩形的纵向偏移量
return 20;
})
.text(function(d){//text 的内容
return d;
})
.attr("y",function(d){//变化前y的坐标
var min = yScale.domain()[0];
return yScale(min);
})
.transition()//过渡
.delay(function(d,i){
return i * 200;
})
.duration(2000)
.ease("bounce")
.attr("y",function(d){//变化后的y坐标
return yScale(d);
});
//添加x轴,分组元素<g>
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + (height - padding.bottom) + ")")
.call(xAxis);
//添加y轴,分组元素<g>
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.call(yAxis);
</script>
</body>
</html>
(九)布局
1、布局是什么
布局,英文是 Layout。
从字面看,可以想到有“决定什么元素绘制在哪里”的意思。
布局的作用是:将不适合用于绘图的数据转换成了适合用于绘图的数据。
2、布局有哪些
D3 总共提供了 12 个布局:
饼状图(Pie)、
力导向图(Force)、
弦图(Chord)、
树状图(Tree)、
集群图(Cluster)、
捆图(Bundle)、
打包图(Pack)、
直方图(Histogram)、
分区图(Partition)、
堆栈图(Stack)、
矩阵树图(Treemap)、
层级图(Hierarchy)。
3、饼状图的制作
本文制作一个饼状图。在布局的应用中,最简单的就是饼状图,通过本文你将对布局有一个初步了解。
源代码:
<html>
<head>
<meta charset="utf-8">
<title>饼状图</title>
</head>
<style>
</style>
<body>
<script src="d3.min.js" charset="utf-8"></script>
<script>
var width = 400;
var height = 400;
var dataset = [ 110,30 , 10 , 43 , 55 , 13 ];
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var pie = d3.layout.pie();
var piedata = pie(dataset);
var outerRadius = 150; //外半径
var innerRadius = 0; //内半径,为0则中间没有空白
var arc = d3.svg.arc() //弧生成器
.innerRadius(innerRadius) //设置内半径
.outerRadius(outerRadius); //设置外半径
var color = d3.scale.category10();
var arcs = svg.selectAll("g")
.data(piedata)
.enter()
.append("g")
.attr("transform","translate("+ (width/2) +","+ (width/2) +")");
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.data;
});
</script>
</body>
</html>
4、
力导向图的制作
力导向图中每一个节点都受到力的作用而运动,这种是一种非常绚丽的图表 。
力导向图(Force-Directed Graph),是绘图的一种算法。在二维或三维空间里配置节点,节点之间用线连接,称为连线。各连线的长度几乎相等,且尽可能不相交。节点和连线都被施加了力的作用,力是根据节点和连线的相对位置计算的。根据力的作用,来计算节点和连线的运动轨迹,并不断降低它们的能量,最终达到一种能量很低的安定状态。
力导向图能表示节点之间的多对多的关系。
源代码:
<html>
<head>
<meta charset="utf-8">
<title>力导向图</title>
</head>
<style>
</style>
<body>
<script src="d3.min.js" charset="utf-8"></script>
<script>
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 width = 400;
var height = 400;
var svg = d3.select("body")
.append("svg")
.attr("width",width)
.attr("height",height);
var force = d3.layout.force()
.nodes(nodes) //指定节点数组
.links(edges) //指定连线数组
.size([width,height]) //指定范围
.linkDistance(150) //指定连线长度
.charge(-400); //相互之间的作用力
force.start(); //开始作用
console.log(nodes);
console.log(edges);
//添加连线
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;
});
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; });
});
</script>
</body>
</html>
5、弦图的制作
弦图是一种用于描述节点之间联系的图表。
弦图(Chord),主要用于表示两个节点之间的联系。
两点之间的连线,表示谁和谁具有联系
线的粗细表示权重
源代码:
<html>
<head>
<meta charset="utf-8">
<title>弦图</title>
<style>
.chord path {
fill-opacity: 0.67;
stroke: #000;
stroke-width: 0.5px;
}
</style>
</head>
<body>
<script src="d3.min.js"></script>
<script>
//1.定义数据
// 城市名
var city_name = [ "北京" , "上海" , "广州" , "深圳" , "香港" ];
// 城市人口的来源,如
// 北京 上海
// 北京 1000 3045
// 上海 3214 2000
// 表示北京市的人口有1000个人来自本地,有3045人是来自上海的移民,总人口为 1000 + 3045
// 上海市的人口有2000个人来自本地,有3214人是来自北京的移民,总人口为 3214 + 2000
var population = [
[ 1000, 3045 , 4567 , 1234 , 3714 ],
[ 3214, 2000 , 2060 , 124 , 3234 ],
[ 8761, 6545 , 3000 , 8045 , 647 ],
[ 3211, 1067 , 3214 , 4000 , 1006 ],
[ 2146, 1034 , 6745 , 4764 , 5000 ]
];
//2.转换数据,并输出转换后的数据
var chord_layout = d3.layout.chord()
.padding(0.03) //节点之间的间隔
.sortSubgroups(d3.descending) //排序
.matrix(population); //输入矩阵
var groups = chord_layout.groups();
var chords = chord_layout.chords();
console.log( groups );
console.log( chords );
//3.SVG,弦图,颜色函数的定义
var width = 600;
var height = 600;
var innerRadius = width/2 * 0.7;
var outerRadius = innerRadius * 1.1;
var color20 = d3.scale.category20();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width/2 + "," + height/2 + ")");
//4.绘制节点(即分组,有多少个城市画多少个弧形),及绘制城市名称
var outer_arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var g_outer = svg.append("g");
g_outer.selectAll("path")
.data(groups)
.enter()
.append("path")
.style("fill", function(d) { return color20(d.index); })
.style("stroke", function(d) { return color20(d.index); })
.attr("d", outer_arc );
g_outer.selectAll("text")
.data(groups)
.enter()
.append("text")
.each( function(d,i) {
d.angle = (d.startAngle + d.endAngle) / 2;
d.name = city_name[i];
})
.attr("dy",".35em")
.attr("transform", function(d){
return "rotate(" + ( d.angle * 180 / Math.PI ) + ")" +
"translate(0,"+ -1.0*(outerRadius+10) +")" +
( ( d.angle > Math.PI*3/4 && d.angle < Math.PI*5/4 ) ? "rotate(180)" : "");
})
.text(function(d){
return d.name;
});
//5.绘制内部弦(即所有城市人口的来源,即有5*5=25条弧)
var inner_chord = d3.svg.chord()
.radius(innerRadius);
svg.append("g")
.attr("class", "chord")
.selectAll("path")
.data(chords)
.enter()
.append("path")
.attr("d", inner_chord )
.style("fill", function(d) { return color20(d.source.index); })
.style("opacity", 1)
.on("mouseover",function(d,i){
d3.select(this)
.style("fill","yellow");
})
.on("mouseout",function(d,i) {
d3.select(this)
.transition()
.duration(1000)
.style("fill",color20(d.source.index));
});
</script>
</body>
</html>
6、集群图的制作
集群图,是一种用于表示包含与被包含关系的图表。
源代码:
<html>
<head>
<meta charset="utf-8">
<title>集群图</title>
<style>
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 1.5px;
}
.node {
font: 12px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 1.5px;
}
</style>
</head>
<body>
<script src="d3.min.js"></script>
<script>
var width = 500,
height = 500;
var cluster = d3.layout.cluster()
.size([width, height - 200]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(40,0)");
d3.json("city.json", function(error, root) {
var nodes = cluster.nodes(root);
var links = cluster.links(nodes);
console.log(nodes);
console.log(links);
var link = svg.selectAll(".link")
.data(links)
.enter()
.append("path")
.attr("class", "link")
.attr("d", diagonal);
var node = svg.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
node.append("circle")
.attr("r", 4.5);
node.append("text")
.attr("dx", function(d) { return d.children ? -8 : 8; })
.attr("dy", 3)
.style("text-anchor", function(d) { return d.children ? "end" : "start"; })
.text(function(d) { return d.name; });
});
</script>
</body>
</html>
7、树状图的制作
树状图( Tree )用于表示层级、上下级、包含与被包含关系。
源代码:
<html>
<head>
<meta charset="utf-8">
<title>树状图</title>
<style>
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 1.5px;
}
.node {
font: 12px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 1.5px;
}
</style>
</head>
<body>
<script src="d3.min.js"></script>
<script>
var width = 500,
height = 500;
var tree = d3.layout.tree()
.size([width, height-200])
.separation(function(a, b) { return (a.parent == b.parent ? 1 : 2); });
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(40,0)");
d3.json("city_tree.json", function(error, root) {
var nodes = tree.nodes(root);
var links = tree.links(nodes);
console.log(nodes);
console.log(links);
var link = svg.selectAll(".link")
.data(links)
.enter()
.append("path")
.attr("class", "link")
.attr("d", diagonal);
var node = svg.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
node.append("circle")
.attr("r", 4.5);
node.append("text")
.attr("dx", function(d) { return d.children ? -8 : 8; })
.attr("dy", 3)
.style("text-anchor", function(d) { return d.children ? "end" : "start"; })
.text(function(d) { return d.name; });
});
</script>
</body>
</html>
8、打包图的制作
打包图( Pack ),用于表示包含与被包含的关系,也可表示各对象的权重,通常用一圆套一圆来表示前者,用圆的大小来表示后者。
源代码:
<html>
<head>
<meta charset="utf-8">
<title>Pack</title>
</head>
<style>
</style>
<body>
<script src="d3.min.js"></script>
<script>
var width = 500;
var height = 500;
var pack = d3.layout.pack()
.size([ width, height ])
.radius(20);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(0,0)");
d3.json("city2.json", function(error, root) {
var nodes = pack.nodes(root);
var links = pack.links(nodes);
console.log(nodes);
console.log(links);
svg.selectAll("circle")
.data(nodes)
.enter()
.append("circle")
.attr("fill","rgb(31, 119, 180)")
.attr("fill-opacity","0.4")
.attr("cx",function(d){
return d.x;
})
.attr("cy",function(d){
return d.y;
})
.attr("r",function(d){
return d.r;
})
.on("mouseover",function(d,i){
d3.select(this)
.attr("fill","yellow");
})
.on("mouseout",function(d,i){
d3.select(this)
.attr("fill","rgb(31, 119, 180)");
});
svg.selectAll("text")
.data(nodes)
.enter()
.append("text")
.attr("font-size","10px")
.attr("fill","white")
.attr("fill-opacity",function(d){
if(d.depth == 2)
return "0.9";
else
return "0";
})
.attr("x",function(d){ return d.x; })
.attr("y",function(d){ return d.y; })
.attr("dx",-12)
.attr("dy",1)
.text(function(d){ return d.name; });
});
</script>
</body>
</html>
9、地图的制作
本章以中国地图为例,介绍地图的制作方法。
在数据可视化中,地图是很重要的一部分。很多情况会与地图有关联,如中国各省的人口多少,GDP多少等,都可以和地图联系在一起。
源代码:
<html>
<head>
<meta charset="utf-8">
<title>中国地图</title>
</head>
<style>
</style>
<body>
<script src="d3.min.js"></script>
<script>
var width = 1000;
var height = 1000;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(0,0)");
var projection = d3.geo.mercator()
.center([107, 31])
.scale(850)
.translate([width/2, height/2]);
var path = d3.geo.path()
.projection(projection);
var color = d3.scale.category20();
d3.json("china.geojson", 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));
});
});
</script>
</body>
</html>